Estoy totalmente de acuerdo con el punto de vista OP. Assert.assertFalse(expected.equals(actual))
No es una forma natural de expresar una desigualdad.
Pero diría que, además Assert.assertEquals()
, Assert.assertNotEquals()
funciona , pero no es fácil de usar para documentar lo que la prueba realmente afirma y entender / depurar cuando falla la afirmación.
Entonces sí JUnit 4.11 y JUnit 5 proporciona Assert.assertNotEquals()
( Assertions.assertNotEquals()
en JUnit 5) pero realmente evito usarlos.
Como alternativa, para afirmar el estado de un objeto, generalmente uso una API de comparación que profundiza en el estado del objeto fácilmente, documenta claramente la intención de las afirmaciones y es muy fácil de entender para el usuario la causa de la falla de la afirmación.
Aquí hay un ejemplo.
Supongamos que tengo una clase Animal en la que quiero probar el createWithNewNameAndAge()
método, un método que crea un nuevo objeto Animal cambiando su nombre y su edad pero manteniendo su comida favorita.
Supongamos que uso Assert.assertNotEquals()
para afirmar que el original y los nuevos objetos son diferentes.
Aquí está la clase Animal con una implementación defectuosa 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+ (o JUnit 5) tanto como corredor de prueba como herramienta de afirmación
@Test
void assertListNotEquals_JUnit_way() {
Animal scoubi = new Animal("scoubi", 10, "hay");
Animal littleScoubi = scoubi.createWithNewNameAndAge("little scoubi", 1);
Assert.assertNotEquals(scoubi, littleScoubi);
}
La prueba falla como se esperaba, pero la causa proporcionada al desarrollador realmente no es útil. Simplemente dice que los valores deben ser diferentes y generar el toString()
resultado invocado en el Animal
parámetro real :
java.lang.AssertionError: los valores deben ser diferentes. Reales: animales
[nombre = scoubi, edad = 10, comida favorita = heno]
en org.junit.Assert.fail (Assert.java:88)
Ok, los objetos no son iguales. ¿Pero dónde está el problema?
¿Qué campo no está correctamente valorado en el método probado? Uno ? Dos? Todos ellos ?
Para descubrirlo, debe profundizar en la createWithNewNameAndAge()
implementación / usar un depurador, mientras que la API de prueba sería mucho más amigable si nos hiciera la diferencia entre lo que se espera y lo que se obtiene.
JUnit 4.11 como corredor de prueba y una API de Matcher de prueba como herramienta de afirmación
Aquí el mismo escenario de prueba pero que usa AssertJ (una excelente API de comparación de pruebas) para hacer la afirmación del 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");
}
Por supuesto, la prueba todavía falla, pero esta vez la razón está claramente establecida:
java.lang.AssertionError:
Esperando:
<["scoubi", 10, "heno"]>
para contener exactamente (y en el mismo orden):
<["pequeño scoubi", 1, "heno"]>
pero no se encontraron algunos elementos:
<["pequeño scoubi", 1]>
y no se esperaban otros:
<["scoubi", 10]>
en junit5.MyTest.assertListNotEquals_AssertJ (MyTest.java:26)
Podemos leer que para los Animal::getName, Animal::getAge, Animal::getFavoriteFood
valores del Animal devuelto, esperamos tener estos valores:
"little scoubi", 1, "hay"
pero hemos tenido estos valores:
"scoubi", 10, "hay"
Entonces sabemos dónde investigar: name
y age
no se valoran correctamente. Además, el hecho de especificar el hay
valor en la afirmación de Animal::getFavoriteFood()
permite también afirmar más finamente el devuelto Animal
. Queremos que los objetos no sean los mismos para algunas propiedades, pero no necesariamente para todas las propiedades.
Definitivamente, usar una API de comparación es mucho más claro y flexible.