Perché JUnit non fornisce i metodi assertNotEquals?


429

Qualcuno sa perché JUnit 4 fornisce assertEquals(foo,bar)ma non i assertNotEqual(foo,bar)metodi?

Fornisce assertNotSame(corrispondente a assertSame) e assertFalse(corrispondente a assertTrue), quindi sembra strano che non si siano preoccupati di includerlo assertNotEqual.

A proposito, so che JUnit-addons fornisce i metodi che sto cercando. Sto solo chiedendo per curiosità.


Almeno da JUnit 4.12, viene fornito assertNotEquals. junit.org/junit4/javadoc/4.12/org/junit/…
WebF0x

Risposte:


403

Ti suggerirei di utilizzare assertThat()le asserzioni di stile più recenti , che possono facilmente descrivere tutti i tipi di negazioni e creare automaticamente una descrizione di ciò che ti aspettavi e di ciò che hai se l'asserzione fallisce:

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

Tutte e tre le opzioni sono equivalenti, scegli quella che ritieni più leggibile.

Per utilizzare i nomi semplici dei metodi (e consentire a questa sintassi tesa di funzionare), sono necessarie le seguenti importazioni:

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

134
Apprezzo il puntatore alla sintassi dell'asserzione alternativa, ma indicare altrove non risponde al motivo per cui JUnit non ha mai fornito assertNotEquals().
seh

14
@seh: Il modo in cui l'ho letto la domanda non riguardava l'interesse storico, ma un modo per formulare l'affermazione "questi due oggetti non sono uguali" in un test JUnit. Ho risposto a quello. Considerando il "perché non c'è / non c'era assertNotEqual" direi che è perché si tratta di un'affermazione specializzata che non è necessaria tanto spesso quanto assertEqualse quindi sarebbe espressa tramite il generico assertFalse.
Joachim Sauer,

21
"scegli quello che trovi più leggibile". Le persone che leggono e scrivono unit test sono programmatori. Lo trovano veramente più leggibile di assertNotEqual (objectUnderTest, someOtherObject) o assertFalse (objectUnderTest.equals (someOtherObject))? Non sono convinto dalle fantasiose API del matcher - sembra essere molto più difficile per un programmatore esplorare / scoprire come usarle ...
bacar

@bacar: per alcuni asserzioni è fondamentalmente una questione di stile. Ma assertThatè molto più espressivo dell'insieme limitato di assert*metodi disponibili. Quindi puoi esprimere i vincoli esatti in una sola riga, farlo leggere (quasi) come una frase inglese e ottenere un messaggio significativo quando l'asserzione fallisce. Certo, non è sempre una caratteristica killer, ma quando l'hai vista in azione alcune volte, vedrai quanto valore aggiunge.
Joachim Sauer

5
@Joachim Sono d'accordo che assertThatsia più espressivo di assert*, ma non penso che sia più espressivo dell'espressione java che puoi mettere dentro e fuori assert*dall'espressione in generale (dopo tutto posso esprimere qualsiasi cosa nel codice java). È un problema generale che ho iniziato a riscontrare con API in stile fluente: ognuno è fondamentalmente un nuovo DSL che devi imparare (quando tutti già conosciamo quello Java!). Suppongo che Hamcrest sia abbastanza onnipresente ora che è ragionevole aspettarsi che la gente lo sappia. Farò uno spettacolo ...
Bacar

154

Ce assertNotEqualsn'è uno in 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;

1
Mente, uno dei manufatti jvenck (2.6.0) perde una vecchia versione di junit-dep che a sua volta ha una classe Assert senza assertNotEquals. Meglio escluderlo quando si usa l'edera.
gkephorus,

7
Sto usando 4.12 ma non riesco ancora a trovare assertNotEqual. : s
Mubashar,

49

Mi chiedo lo stesso. L'API di Assert non è molto simmetrica; per verificare se gli oggetti sono uguali, fornisce assertSamee assertNotSame.

Certo, non è troppo lungo per scrivere:

assertFalse(foo.equals(bar));

Con tale affermazione, l'unica parte informativa dell'output è purtroppo il nome del metodo di prova, quindi il messaggio descrittivo dovrebbe essere formato separatamente:

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

Questo è ovviamente così noioso, che è meglio arrotolarti assertNotEqual. Fortunatamente in futuro sarà forse parte del numero 22 di JUnit: JUnit


19
Ma questo è meno utile, perché JUnit non può generare un utile messaggio di errore che ti dice, ad esempio, i valori disuguali di foo e bar. Il vero motivo del fallimento è nascosto e trasformato in un semplice booleano.
Ben James,

3
Sono totalmente d'accordo. Soprattutto assertFalse ha bisogno dell'argomento messaggio corretto per produrre output per dire cosa è andato storto.
Mikko Maunu,

Penso che questo sia utile per i test di presente testo. Thnx
Marouane Gazanayi

Il problema con il testo è che non sarà aggiornato con l'evoluzione del codice.
Mark Levison,

13

Direi che l'assenza di assertNotEqual è davvero un'asimmetria e rende JUnit un po 'meno apprendibile. Ricorda che questo è un caso chiaro quando l'aggiunta di un metodo ridurrebbe la complessità dell'API, almeno per me: la simmetria aiuta a governare lo spazio più grande. La mia ipotesi è che la ragione dell'omissione potrebbe essere che ci sono troppe poche persone che chiedono il metodo. Eppure, ricordo un momento in cui persino affermare False non esisteva; quindi, ho un'aspettativa positiva che il metodo potrebbe eventualmente essere aggiunto, dato che non è difficile; anche se riconosco che ci sono numerose soluzioni alternative, anche eleganti.


7

Vengo a questa festa abbastanza tardi ma ho trovato che il modulo:

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

può essere fatto funzionare per la maggior parte dei casi "non uguali".

int status = doSomething() ; // expected to return 123
assertTrue("doSomething() returned unexpected status", status != 123 ) ;

4
Mentre questo funziona, il problema è che se l'asserzione fallisce, dirà semplicemente "Eseguito vero, ma era falso", o qualche altra affermazione poco chiara. Sarebbe fantastico se fosse previsto Non 123, ma 123.
Rabbino

6

Sto lavorando su JUnit in ambiente Java 8, usando jUnit4.12

per me: il compilatore non è stato in grado di trovare il metodo assertNotEquals, anche quando l'ho usato
import org.junit.Assert;

Quindi sono passato
assertNotEquals("addb", string);
a
Assert.assertNotEquals("addb", string);

Quindi, se stai affrontando un problema relativo a assertNotEqualnon riconosciuto, cambiarlo in Assert.assertNotEquals(,);esso dovrebbe risolvere il tuo problema


1
Questo perché i metodi sono statici e devi importarli staticamente. Usa questo import static org.junit.Assert.*;e sarai in grado di usare tutti gli asserti senza il nome della classe.
Tom Stone,

3

La ragione ovvia che la gente voleva assertNotEquals () era di confrontare i builtin senza prima convertirli in oggetti in piena regola:

Esempio dettagliato:

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

vs.

assertNotEqual(1, winningBidderId);

Purtroppo, dal momento che Eclipse non include JUnit 4.11 per impostazione predefinita, devi essere prolisso.

Avvertenza Non penso che l '"1" debba essere racchiuso in un Integer.valueOf () ma dato che sono appena tornato da .NET non conto della mia correttezza.


1

È meglio usare l'Hamcrest per affermazioni negative piuttosto che assertFalse poiché nel primo il rapporto di prova mostrerà una differenza per il fallimento dell'asserzione.

Se si utilizza assertFalse, nel report viene visualizzato semplicemente un errore di asserzione. vale a dire informazioni perse a causa del fallimento.


1

Di solito lo faccio quando mi aspetto che due oggetti siano uguali:

assertTrue(obj1.equals(obj2));

e:

assertFalse(obj1.equals(obj2));

quando ci si aspetta che siano ineguali. Sono consapevole che questa non è una risposta alla tua domanda, ma è la più vicina che posso ottenere. Potrebbe aiutare gli altri a cercare cosa possono fare nelle versioni di JUnit precedenti a JUnit 4.11.


0

Concordo pienamente con il punto di vista dell'OP. Assert.assertFalse(expected.equals(actual))non è un modo naturale per esprimere una disuguaglianza.
Ma direi che oltre a ciò funziona Assert.assertEquals(), Assert.assertNotEquals()ma non è facile da usare per documentare ciò che il test afferma effettivamente e per capire / eseguire il debug in quanto l'affermazione fallisce.
Quindi sì, JUnit 4.11 e JUnit 5 forniscono Assert.assertNotEquals()( Assertions.assertNotEquals()in JUnit 5) ma evito davvero di usarli.

In alternativa, per affermare lo stato di un oggetto, utilizzo generalmente un'API di corrispondenza che scava facilmente nello stato oggetto, documentando chiaramente l'intenzione delle asserzioni e che è molto intuitivo per comprendere la causa dell'errore dell'asserzione.

Ecco un esempio
Supponiamo che io abbia una classe Animal che voglio testare il createWithNewNameAndAge()metodo, un metodo che crea un nuovo oggetto Animal cambiando il suo nome e la sua età ma mantenendo il suo cibo preferito.
Supponiamo che io Assert.assertNotEquals()affermi che l'originale e i nuovi oggetti sono diversi.
Ecco la classe Animal con un'implementazione imperfetta di 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) sia come test runner che come strumento di asserzione

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

Il test ha esito negativo come previsto, ma la causa fornita allo sviluppatore non è davvero utile. Dice solo che i valori dovrebbero essere diversi e produrre il toString()risultato invocato sul Animalparametro effettivo :

java.lang.AssertionError: i valori dovrebbero essere diversi. Attuale: animale

[nome = scoubi, età = 10, cibo preferito = fieno]

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

Ok gli oggetti non sono uguali. Ma dov'è il problema?
Quale campo non è valutato correttamente nel metodo testato? Uno ? Due ? Tutti loro ?
Per scoprirlo devi scavare createWithNewNameAndAge() nell'implementazione / usare un debugger mentre l'API di test sarebbe molto più amichevole se ci farebbe il differenziale tra ciò che è previsto e ciò che è ottenuto.


JUnit 4.11 come test runner e un'API Matcher di test come strumento di asserzione

Qui lo stesso scenario di test ma che utilizza AssertJ (un'eccellente API di abbinamento test) per fare l'affermazione dello Animalstato::

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

Naturalmente il test fallisce ancora, ma questa volta il motivo è chiaramente indicato:

java.lang.AssertionError:

Aspetta:

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

per contenere esattamente (e nello stesso ordine):

<["little scoubi", 1, "fieno"]>

ma alcuni elementi non sono stati trovati:

<["little scoubi", 1]>

e altri non erano previsti:

<["scoubi", 10]>

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

Possiamo leggere che per i Animal::getName, Animal::getAge, Animal::getFavoriteFoodvalori dell'Animale restituito, prevediamo di avere questo valore:

"little scoubi", 1, "hay" 

ma abbiamo avuto questi valori:

"scoubi", 10, "hay"

Quindi sappiamo dove indagare: namee agenon sono valutati correttamente. Inoltre, il fatto di specificare il hayvalore nell'affermazione diAnimal::getFavoriteFood() consente anche di affermare più finemente il reso Animal. Vogliamo che gli oggetti non siano gli stessi per alcune proprietà ma non necessariamente per tutte le proprietà.
Quindi sicuramente, usare un'API matcher è molto più chiaro e flessibile.


-1

Coerenza API Modulo, perché JUnit non ha fornito assertNotEquals()è lo stesso motivo per cui JUnit non ha mai fornito metodi come

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

cioè non c'è fine a fornire metodi di asserzione specifici per i tipi di cose che potresti desiderare nella tua logica di asserzione!

Molto meglio per fornire le primitive di prova componibile come equalTo(...), is(...), not(...), regex(...)e lasciare che il pezzo programmatore quelli insieme invece per più leggibilità e la sanità mentale.


3
bene, per qualche ragione, esiste assertEquals (). Non è stato necessario, ma lo è. La domanda riguardava la mancanza di simmetria: perché esiste AssertEquals ma non la sua controparte?
Foo
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.