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 Animal
parâ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 Animal
estado:
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::getFavoriteFood
valores do Animal retornado, esperamos ter o seguinte valor:
"little scoubi", 1, "hay"
mas tivemos esses valores:
"scoubi", 10, "hay"
Portanto, sabemos onde investigar: name
e age
não são corretamente avaliados. Além disso, o fato de especificar o hay
valor 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.