Por que o JUnit não fornece métodos assertNotEquals?

429

Alguém sabe por que o JUnit 4 fornece, assertEquals(foo,bar)mas não assertNotEqual(foo,bar)métodos?

Ele fornece assertNotSame(correspondente a assertSame) e assertFalse(correspondente a assertTrue), portanto, parece estranho que eles não se incomodassem em incluir assertNotEqual.

A propósito, eu sei que o JUnit-addons fornece os métodos que estou procurando. Só estou perguntando por curiosidade.

Chris B
fonte
Pelo menos desde a JUnit 4.12, assertNotEquals é fornecido. junit.org/junit4/javadoc/4.12/org/junit/…
WebF0x 25/10/1918

Respostas:

403

Eu sugiro que você use as declarações de assertThat()estilo mais recentes , que podem descrever facilmente todos os tipos de negações e criar automaticamente uma descrição do que você esperava e do que conseguiu se a declaração falhar:

assertThat(objectUnderTest, is(not(someOtherObject)));
assertThat(objectUnderTest, not(someOtherObject));
assertThat(objectUnderTest, not(equalTo(someOtherObject)));

Todas as três opções são equivalentes, escolha a que você achar mais legível.

Para usar os nomes simples dos métodos (e permitir que essa sintaxe tensa funcione), você precisa destas importações:

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
Joachim Sauer
fonte
134
Aprecio o ponteiro para a sintaxe de asserção alternativa, mas apontar para outro lugar não responde por que o JUnit nunca forneceu assertNotEquals().
seh
14
@seh: A maneira como eu li a pergunta não era sobre interesse histórico, mas sobre uma maneira de formular a afirmação "esses dois objetos não são iguais" em um teste JUnit. Eu respondi isso. Considerando o "por que não há / não existe assertNotEqual", eu diria que é porque é uma afirmação especializada que não é necessária com a frequência necessária assertEqualse, portanto, seria expressa por meio do genérico assertFalse.
Joachim Sauer
21
"escolha o que você achar mais legível". As pessoas que leem e escrevem testes de unidade são programadores. Eles realmente acham isso mais legível do que assertNotEqual (objectUnderTest, someOtherObject) ou assertFalse (objectUnderTest.equals (someOtherObject))? Eu não estou convencido pelos APIs fantasia Matcher - parece ser consideravelmente mais difícil para um programador para explorar / descobrir como usá-los ...
Bacar
@bacar: para alguns afirma que é basicamente uma questão de estilo. Mas assertThaté muito mais expressivo do que o conjunto limitado de assert*métodos disponíveis. Portanto, você pode expressar as restrições exatas em uma única linha, ler (quase) como uma frase em inglês e receber uma mensagem significativa quando a declaração falhar. É verdade que nem sempre é um recurso matador, mas quando você o vê em ação algumas vezes, verá quanto valor agrega.
Joachim Sauer
5
@ Joachim Concordo que assertThaté mais expressivo do que assert*, mas não acho que seja mais expressivo do que a expressão java que você pode colocar dentro e fora da assert*expressão em geral (afinal, eu posso expressar qualquer coisa no código java). É um problema geral que eu comecei a encontrar com APIs de estilo fluente - cada uma é basicamente um novo DSL que você precisa aprender (quando todos já conhecemos o Java!). Suponho que Hamcrest seja onipresente o suficiente agora que é razoável esperar que as pessoas o conheçam. Eu vou ter um jogo ...
Bacar
154

Existe um assertNotEqualsno JUnit 4.11: https://github.com/junit-team/junit/blob/master/doc/ReleaseNotes4.11.md#improvements-to-assert-and-assume

import static org.junit.Assert.assertNotEquals;
Stefan Birkner
fonte
1
Lembre-se, um dos artefatos do jmock (2.6.0) vaza uma versão antiga do junit-dep que, por sua vez, possui uma classe Assert sem os assertNotEquals. Melhor excluir isso ao usar a hera.
Gkephorus #
7
Estou usando a versão 4.12, mas ainda não consegui encontrar assertNotEqual. : s
Mubashar
49

Eu me pergunto o mesmo. A API do Assert não é muito simétrica; para testar se os objetos são iguais, ele fornece assertSameeassertNotSame .

Obviamente, não demorou muito para escrever:

assertFalse(foo.equals(bar));

Com essa afirmação, infelizmente, a única parte informativa da saída é o nome do método de teste; portanto, a mensagem descritiva deve ser formada separadamente:

String msg = "Expected <" + foo + "> to be unequal to <" + bar +">";
assertFalse(msg, foo.equals(bar));

É claro que é tão tedioso que é melhor rolar o seu próprio assertNotEqual. Felizmente, no futuro, talvez faça parte do JUnit: JUnit issue 22

Mikko Maunu
fonte
19
Mas isso é menos útil, porque o JUnit não pode gerar uma mensagem de falha útil informando, por exemplo, os valores desiguais de foo e bar. O verdadeiro motivo da falha é oculto e transformado em um booleano simples.
Ben James
3
Eu concordo totalmente. Especialmente assertFalse precisa de um argumento de mensagem apropriado para produzir saída e dizer o que realmente deu errado.
Mikko Maunu
Eu acho que isso é útil para os testes de texto presentes. Thnx
Marouane Gazanayi
O problema com o texto é que ele ficará desatualizado à medida que o código evoluir.
Mark Levison
13

Eu diria que a ausência de assertNotEqual é de fato uma assimetria e torna a JUnit um pouco menos aprendível. Lembre-se de que esse é um caso interessante ao adicionar um método diminuiria a complexidade da API, pelo menos para mim: a simetria ajuda a controlar o espaço maior. Meu palpite é que o motivo da omissão pode ser que haja muito poucas pessoas pedindo o método. Ainda assim, lembro-me de uma época em que mesmo afirmarFalse não existia; portanto, tenho uma expectativa positiva de que o método possa eventualmente ser adicionado, uma vez que não é difícil; embora reconheça que existem várias soluções alternativas, mesmo as mais elegantes.

Bernhard Bodenstorfer
fonte
7

Estou chegando a esta festa muito tarde, mas descobri que o formulário:

static void assertTrue(java.lang.String message, boolean condition) 

pode ser feito para funcionar na maioria dos casos 'não iguais'.

int status = doSomething() ; // expected to return 123
assertTrue("doSomething() returned unexpected status", status != 123 ) ;
user903724
fonte
4
Enquanto isso funciona, o problema é que, se a afirmação falhar, ela simplesmente dirá "Excepto verdadeiro, mas era falso" ou alguma outra declaração pouco clara. O que seria ótimo é se o esperado era não 123, mas foi 123.
Discrição rabino
6

Estou trabalhando no JUnit no ambiente java 8, usando o jUnit4.12

para mim: o compilador não conseguiu encontrar o método assertNotEquals, mesmo quando eu usei
import org.junit.Assert;

Então eu mudei
assertNotEquals("addb", string);
para
Assert.assertNotEquals("addb", string);

Portanto, se você estiver enfrentando um problema em relação ao assertNotEqualnão reconhecido, altere-o para que Assert.assertNotEquals(,);ele resolva o seu problema

Akshay Vijay Jain
fonte
1
Isso ocorre porque os métodos são estáticos e você deve importá-los estaticamente. Use isso import static org.junit.Assert.*;e você poderá usar todas as declarações sem o nome da classe.
Tom Stone
3

A razão óbvia pela qual as pessoas queriam assertNotEquals () era comparar os componentes internos sem precisar convertê-los em objetos completos antes:

Exemplo detalhado:

....
assertThat(1, not(equalTo(Integer.valueOf(winningBidderId))));
....

vs.

assertNotEqual(1, winningBidderId);

Infelizmente, como o Eclipse não inclui o JUnit 4.11 por padrão, você deve estar detalhado.

Advertência Eu não acho que o '1' precise ser envolvido em um Integer.valueOf (), mas desde que eu retornei do .NET, não conte com a minha correção.

Mark Levison
fonte
1

É melhor usar o Hamcrest para afirmações negativas, em vez de assertFalse, pois no primeiro caso o relatório de teste mostrará uma diferença para a falha de afirmação.

Se você usar assertFalse, você apenas terá uma falha de afirmação no relatório. isto é, informações perdidas sobre a causa da falha.

Chris Kelly
fonte
1

Geralmente faço isso quando espero que dois objetos sejam iguais:

assertTrue(obj1.equals(obj2));

e:

assertFalse(obj1.equals(obj2));

quando se espera que sejam desiguais. Estou ciente de que isso não é uma resposta para sua pergunta, mas é o mais próximo que posso chegar. Isso pode ajudar outras pessoas a procurar o que podem fazer nas versões do JUnit anteriores ao JUnit 4.11.

Willempie
fonte
0

Eu concordo totalmente com o ponto de vista do OP. Assert.assertFalse(expected.equals(actual))não é uma maneira natural de expressar uma desigualdade.
Mas eu argumentaria que, além do mais Assert.assertEquals(), Assert.assertNotEquals()funciona , mas não é amigável, documentar o que o teste realmente afirma e entender / depurar quando a afirmação falhar.
Então, sim, o JUnit 4.11 e o JUnit 5 fornece Assert.assertNotEquals()(Assertions.assertNotEquals() no JUnit 5), mas eu realmente evito usá-los.

Como alternativa, para afirmar o estado de um objeto, geralmente uso uma API de correspondência que explora facilmente o estado do objeto, que documenta claramente a intenção das afirmações e é muito amigável para entender a causa da falha de afirmação.

Aqui está um exemplo.
Suponha que eu tenha uma classe Animal na qual desejo testar o createWithNewNameAndAge()método, um método que cria um novo objeto Animal alterando seu nome e sua idade, mas mantendo sua comida favorita.
Suponha que eu use Assert.assertNotEquals()para afirmar que os objetos originais e os novos são diferentes.
Aqui está a classe Animal com uma implementação falha de createWithNewNameAndAge():

public class Animal {

    private String name;
    private int age;
    private String favoriteFood;

    public Animal(String name, int age, String favoriteFood) {
        this.name = name;
        this.age = age;
        this.favoriteFood = favoriteFood;
    }

    // Flawed implementation : use this.name and this.age to create the 
    // new Animal instead of using the name and age parameters
    public Animal createWithNewNameAndAge(String name, int age) {
        return new Animal(this.name, this.age, this.favoriteFood);
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getFavoriteFood() {
        return favoriteFood;
    }

    @Override
    public String toString() {
        return "Animal [name=" + name + ", age=" + age + ", favoriteFood=" + favoriteFood + "]";
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((favoriteFood == null) ? 0 : favoriteFood.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Animal)) return false;

        Animal other = (Animal) obj;
        return age == other.age && favoriteFood.equals(other.favoriteFood) &&
                name.equals(other.name);
    }

}

JUnit 4.11+ (ou JUnit 5), tanto como executor de teste quanto como ferramenta de asserção

@Test
void assertListNotEquals_JUnit_way() {
    Animal scoubi = new Animal("scoubi", 10, "hay");
    Animal littleScoubi = scoubi.createWithNewNameAndAge("little scoubi", 1);
    Assert.assertNotEquals(scoubi, littleScoubi);
}

O teste falha conforme o esperado, mas a causa fornecida ao desenvolvedor não é realmente útil. Diz apenas que os valores devem ser diferentes e toString()gerar o resultado invocado no Animalparâmetro atual :

java.lang.AssertionError: Os valores devem ser diferentes. Real: Animal

[nome = scoubi, idade = 10, comida favorita = feno]

em org.junit.Assert.fail (Assert.java:88)

Ok, os objetos não são iguais. Mas onde está o problema?
Qual campo não é avaliado corretamente no método testado? 1 ? Dois ? Todos eles ?
Para descobri-lo, você precisa cavar na createWithNewNameAndAge() implementação / usar um depurador, enquanto a API de teste seria muito mais amigável se nos fizesse o diferencial entre o que é esperado e o que é obtido.


JUnit 4.11 como executor de teste e uma API Matcher de teste como ferramenta de asserção

Aqui está o mesmo cenário de teste, mas que usa o AssertJ (uma excelente API de correspondência de teste) para fazer a afirmação do Animalestado:

import org.assertj.core.api.Assertions;

@Test
void assertListNotEquals_AssertJ() {
    Animal scoubi = new Animal("scoubi", 10, "hay");
    Animal littleScoubi = scoubi.createWithNewNameAndAge("little scoubi", 1);
    Assertions.assertThat(littleScoubi)
              .extracting(Animal::getName, Animal::getAge, Animal::getFavoriteFood)
              .containsExactly("little scoubi", 1, "hay");
}

É claro que o teste ainda falha, mas desta vez o motivo está claramente indicado:

java.lang.AssertionError:

Esperando:

<["scoubi", 10, "feno"]>

para conter exatamente (e na mesma ordem):

<["pequeno escocês", 1, "feno"]>

mas alguns elementos não foram encontrados:

<["pequeno escudeiro", 1]>

e outros não eram esperados:

<["scoubi", 10]>

em junit5.MyTest.assertListNotEquals_AssertJ (MyTest.java:26)

Podemos ler que, para Animal::getName, Animal::getAge, Animal::getFavoriteFoodvalores do Animal retornado, esperamos ter o seguinte valor:

"little scoubi", 1, "hay" 

mas tivemos esses valores:

"scoubi", 10, "hay"

Portanto, sabemos onde investigar: namee agenão são corretamente avaliados. Além disso, o fato de especificar o hayvalor na asserção de Animal::getFavoriteFood()permite também afirmar com mais precisão o retorno Animal. Queremos que os objetos não sejam os mesmos para algumas propriedades, mas não necessariamente para todas as propriedades.
Então, definitivamente, o uso de uma API de correspondência é muito mais claro e flexível.

davidxxx
fonte
-1

Consistência da API do módulo, por que o JUnit não forneceu assertNotEquals()é o mesmo motivo pelo qual o JUnit nunca forneceu métodos como

  • assertStringMatchesTheRegex(regex, str) vs. assertStringDoesntMatchTheRegex(regex, str)
  • assertStringBeginsWith(prefix, str) vs. assertStringDoesntBeginWith(prefix, str)

ou seja, não há fim em fornecer métodos de afirmação específicos para os tipos de coisas que você pode querer em sua lógica de afirmação!

Muito melhor para fornecer primitivas teste combináveis como equalTo(...), is(...), not(...), regex(...)e deixar a peça programador aqueles juntos, em vez de mais legibilidade e sanidade.

fatuhoku
fonte
3
bem, por algum motivo, assertEquals () existe. Não precisava, mas precisa. A pergunta era sobre a falta de simetria - por que assertEquals existe, mas não sua contrapartida?
foo