¿Por qué JUnit no proporciona métodos ClaimNotEquals?

429

¿Alguien sabe por qué JUnit 4 proporciona assertEquals(foo,bar)pero no assertNotEqual(foo,bar)métodos?

Proporciona assertNotSame(correspondiente a assertSame) y assertFalse(correspondiente a assertTrue), por lo que parece extraño que no se molesten en incluirlo assertNotEqual.

Por cierto, sé que JUnit-addons proporciona los métodos que estoy buscando. Solo pregunto por curiosidad.

Chris B
fuente
Al menos desde JUnit 4.12, se proporciona afirmarNotEquals. junit.org/junit4/javadoc/4.12/org/junit/…
WebF0x

Respuestas:

403

Te sugiero que uses assertThat()las afirmaciones de estilo más nuevas , que pueden describir fácilmente todo tipo de negaciones y crear automáticamente una descripción de lo que esperabas y lo que obtienes si la afirmación falla:

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

Las tres opciones son equivalentes, elija la que le resulte más legible.

Para usar los nombres simples de los métodos (y permitir que esta sintaxis de tiempo funcione), necesita estas importaciones:

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
Joachim Sauer
fuente
134
Aprecio el puntero a la sintaxis de aserción alternativa, pero señalar en otro lugar no responde por qué JUnit nunca proporcionó assertNotEquals().
seh
14
@seh: La forma en que lo leí no fue sobre el interés histórico, sino sobre una forma de formular la afirmación "estos dos objetos no son iguales" en una prueba JUnit. Yo respondí eso. Teniendo en cuenta el "por qué hay / no hubo assertNotEqual", diría que es porque es una afirmación especializada que no se necesita con tanta frecuencia assertEqualsy, por lo tanto, se expresaría a través del genérico assertFalse.
Joachim Sauer
21
"elige el que te resulte más legible". Las personas que leen y escriben pruebas unitarias son programadores. ¿Realmente encuentran esto más legible que afirmarNotEqual (objectUnderTest, someOtherObject) o afirmarFalse (objectUnderTest.equals (someOtherObject))? No estoy convencido por las API de igualación sofisticadas: parece ser mucho más difícil para un programador explorar / descubrir cómo usarlas ... ''
dijo
@bacar: para algunos afirma que es básicamente una cuestión de estilo. Pero assertThates mucho más expresivo que el conjunto limitado de assert*métodos disponibles. Por lo tanto, puede expresar las restricciones exactas en una sola línea, hacer que (casi) se lea como una oración en inglés y obtener un mensaje significativo cuando la afirmación falla. Por supuesto, esa no siempre es una característica excelente, pero cuando la haya visto en acción varias veces, verá cuánto valor agrega.
Joachim Sauer
55
@Joachim Estoy de acuerdo en que assertThates más expresivo que assert*, pero no creo que sea más expresivo que la expresión java que puedes poner dentro y fuera de la assert*expresión en general (después de todo, puedo expresar cualquier cosa en código java). Es un problema general que he empezado a encontrar con API de estilo fluido: cada una es básicamente una nueva DSL que debes aprender (¡cuando todos ya conocemos la Java!). Sin embargo, supongo que Hamcrest es lo suficientemente ubicuo ahora que es razonable esperar que la gente lo sepa. Voy a jugar ...
bacar
154

Hay un assertNotEqualsen 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
fuente
1
Tenga en cuenta que uno de los artefactos de jmock (2.6.0) maven filtra una versión antigua de junit-dep que a su vez tiene una clase Assert sin la aserciónNotEquals. Mejor excluir eso cuando se usa hiedra.
gkephorus
77
Estoy usando 4.12 pero todavía no puedo encontrar afirmarNotEqual. : s
Mubashar
49

Me pregunto lo mismo. La API de Assert no es muy simétrica; para probar si los objetos son iguales, proporciona assertSamey assertNotSame.

Por supuesto, no es demasiado largo para escribir:

assertFalse(foo.equals(bar));

Con tal afirmación, la única parte informativa de la salida es, lamentablemente, el nombre del método de prueba, por lo que el mensaje descriptivo debe formarse por separado:

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

Eso es, por supuesto, tan tedioso, que es mejor rodar el tuyo assertNotEqual. Por suerte en el futuro, tal vez sea parte de JUnit: JUnit número 22

Mikko Maunu
fuente
19
Pero esto es menos útil, porque JUnit no puede generar un mensaje de falla útil que le diga, por ejemplo, los valores desiguales de foo y bar. La verdadera razón del fracaso se oculta y se convierte en un simple booleano.
Ben James
3
Estoy totalmente de acuerdo. Especialmente afirmar que Falso necesita un argumento de mensaje adecuado para producir resultados que indiquen qué salió realmente mal.
Mikko Maunu
Creo que esto es útil para las pruebas de texto actual. Thnx
Marouane Gazanayi
El problema con el texto es que estará desactualizado a medida que evolucione el código.
Mark Levison
13

Yo diría que la ausencia de afirmarNotEqual es de hecho una asimetría y hace que JUnit sea un poco menos fácil de aprender. Tenga en cuenta que este es un buen caso cuando agregar un método disminuiría la complejidad de la API, al menos para mí: la simetría ayuda a gobernar el espacio más grande. Supongo que la razón de la omisión puede ser que hay muy pocas personas que solicitan el método. Sin embargo, recuerdo un momento en que incluso afirmar Falso no existía; por lo tanto, tengo una expectativa positiva de que el método eventualmente podría agregarse, dado que no es difícil; aunque reconozco que existen numerosas soluciones, incluso elegantes.

Bernhard Bodenstorfer
fuente
7

Llego a esta fiesta bastante tarde, pero he encontrado que la forma:

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

se puede hacer que funcione para la mayoría de los casos "no iguales".

int status = doSomething() ; // expected to return 123
assertTrue("doSomething() returned unexpected status", status != 123 ) ;
usuario903724
fuente
44
Si bien esto funciona, el problema es que si la afirmación falla, simplemente dirá "Expedido verdadero, pero fue falso", o alguna otra declaración poco clara. Lo que sería genial es si se esperaba No 123, sino 123.
Stealth Rabbi
6

Estoy trabajando en JUnit en el entorno java 8, usando jUnit4.12

para mí: el compilador no pudo encontrar el método ClaimNotEquals, incluso cuando usé
import org.junit.Assert;

Entonces cambié
assertNotEquals("addb", string);
a
Assert.assertNotEquals("addb", string);

Entonces, si enfrenta un problema relacionado con assertNotEqualno reconocido, cámbielo a Assert.assertNotEquals(,);él para resolver su problema

Akshay Vijay Jain
fuente
1
Esto se debe a que los métodos son estáticos y debe importarlos estáticamente. Use esto import static org.junit.Assert.*;y podrá usar todas las afirmaciones sin el nombre de la clase.
Tom Stone
3

La razón obvia por la que las personas querían afirmarNotEquals () era comparar las construcciones sin tener que convertirlas primero en objetos completos:

Ejemplo detallado:

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

vs.

assertNotEqual(1, winningBidderId);

Lamentablemente, dado que Eclipse no incluye JUnit 4.11 de forma predeterminada, debe ser detallado.

Advertencia No creo que el '1' deba estar envuelto en un Integer.valueOf (), pero dado que acabo de regresar de .NET, no cuente con mi corrección.

Mark Levison
fuente
1

Es mejor usar Hamcrest para afirmaciones negativas en lugar de afirmar Falso, ya que en el primero el informe de prueba mostrará una diferencia para el fracaso de la afirmación.

Si usa afirmarFalso, solo obtendrá un error de aserción en el informe. es decir, pérdida de información sobre la causa del fallo.

Chris Kelly
fuente
1

Por lo general, hago esto cuando espero que dos objetos sean iguales:

assertTrue(obj1.equals(obj2));

y:

assertFalse(obj1.equals(obj2));

cuando se espera que sean desiguales. Soy consciente de que esta no es una respuesta a su pregunta, pero es lo más cerca que puedo llegar. Podría ayudar a otros a buscar lo que pueden hacer en las versiones de JUnit anteriores a JUnit 4.11.

Willempie
fuente
0

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 Animalpará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 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");
}

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::getFavoriteFoodvalores del Animal devuelto, esperamos tener estos valores:

"little scoubi", 1, "hay" 

pero hemos tenido estos valores:

"scoubi", 10, "hay"

Entonces sabemos dónde investigar: namey ageno se valoran correctamente. Además, el hecho de especificar el hayvalor 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.

davidxxx
fuente
-1

Consistencia de la API de módulo, por qué JUnit no proporcionó assertNotEquals()es la misma razón por la que JUnit nunca proporcionó métodos como

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

es decir, no hay fin de proporcionar métodos de afirmación específicos para el tipo de cosas que puede desear en su lógica de afirmación.

Es mucho mejor para proporcionar primitivas de prueba componibles como equalTo(...), is(...), not(...), regex(...)y dejar que la pieza programador aquellos juntos en lugar de más legibilidad y la cordura.

fatuhoku
fuente
3
bueno, por alguna razón, afirmaEquals () existe. No tenía que hacerlo, pero lo hace. La pregunta se refería a la falta de simetría: ¿por qué afirmar afirman igualdad pero no su contraparte?
foo