JUnit4 fail () ist hier, aber wo ist pass ()?

82

fail()In der JUnit4-Bibliothek gibt es eine Methode. Ich mag es, aber es mangelt an pass()Methoden, die in der Bibliothek nicht vorhanden sind. Wieso ist es so?

Ich habe herausgefunden, dass ich assertTrue(true)stattdessen verwenden kann, aber immer noch unlogisch aussieht.

@Test
 public void testSetterForeignWord(){
  try {
   card.setForeignWord("");
   fail();
  } catch (IncorrectArgumentForSetter ex){
  }

 // assertTrue(true);
 }
Eugene
quelle
7
Verwenden Sie einfach die return-Anweisung - in den meisten Fällen wird diese als pass () übergeben.
Topchef
@topchef dieser einzelne Kommentar traf den Hammer auf den Kopf, während alle anderen darüber debattieren, was akzeptabel ist und welches nicht.
Einige Testsysteme (Perl Test :: Simple) zählen bestandene und fehlgeschlagene Zusicherungen. Junit zählt jedoch die Anzahl der Testmethoden, die bestanden und nicht bestanden wurden. Daher hat Junit nicht die gleiche Verwendung für eine passMethode.
Randall Whitman

Antworten:

63

Solange der Test keine Ausnahme auslöst, besteht er, es sei denn, Ihre @TestAnmerkung gibt eine erwartete Ausnahme an. Ich nehme an, dass a pass()eine spezielle Ausnahme auslösen könnte, die JUnit immer als bestanden interpretiert, um den Test kurzzuschließen, aber dies würde gegen das übliche Design von Tests verstoßen (dh Erfolg annehmen und nur scheitern, wenn eine Behauptung fehlschlägt) und wenn Leute es bekommen Die Idee, dass die Verwendung vorzuziehen ist pass(), würde eine große Anzahl bestehender Tests erheblich verlangsamen (aufgrund des Overheads bei der Erstellung von Ausnahmen). Fehlgeschlagene Tests sollten nicht die Norm sein, daher ist es keine große Sache, wenn sie diesen Overhead haben.

Beachten Sie, dass Ihr Beispiel folgendermaßen umgeschrieben werden könnte:

@Test(expected=IncorrectArgumentForSetter.class)
public void testSetterForeignWord("") throws Exception {
  card.setForeignWord("");
}

Außerdem sollten Sie die Verwendung von Standard-Java-Ausnahmen bevorzugen. Du IncorrectArgumentForSettersolltest wahrscheinlich ein sein IllegalArgumentException.

ColinD
quelle
4
Die Methoden fail () und assertX () lösen wirklich nur einen AssertionError aus, wodurch die Testmethode unrein beendet wird. Deshalb zeigt eine erfolgreiche Rückkehr Erfolg ...
Steven Schlansker
-1 - Die pass () -Methode macht nichts - sie beendet den Test sofort ohne Ausnahmen. Dies entspricht in den meisten Fällen der Rückgabeanweisung (mit Ausnahme der erwarteten Ausnahmen, Zeitüberschreitungen usw.).
Topchef
1
@grigory: Sie haben Recht, dass eine pass()Methode nicht einfach nichts tun kann, damit ein Test nach dem Aufruf nicht fehlschlägt. Also habe ich diesen Satz entfernt.
ColinD
Ich kann dir nicht genug danken, hat mir geholfen, aus viel Frust herauszukommen!
N00b Pr0grammer
70

Rufen Sie die returnAnweisung immer dann an, wenn Ihr Test beendet und bestanden ist.

Horkrux7
quelle
3
+1 muss die richtige Antwort gewesen sein (in den meisten Fällen mit Ausnahme von erwarteten, Zeitüberschreitungen usw.)
topchef
Scheint in der Tat der einfachste und benutzerfreundlichste Weg, einen Test zu beenden.
Benj
7

Ich denke, diese Frage muss aktualisiert werden, da die meisten Antworten hier ziemlich veraltet sind.

Zunächst zur Frage des OP:

Ich denke, es ist ziemlich gut akzeptiert, dass die Einführung des Konzepts der "erwarteten Ausnahme" in JUnit ein schlechter Schachzug war, da diese Ausnahme überall ausgelöst werden könnte und den Test bestehen wird. Es funktioniert, wenn Sie sehr domänenspezifische Ausnahmen auslösen (und geltend machen), aber ich werfe nur solche Ausnahmen aus, wenn ich an Code arbeite, der absolut makellos sein muss. Die meisten APIS lösen einfach die eingebauten Ausnahmen wie IllegalArgumentExceptionoder aus IllegalStateException. Wenn zwei Aufrufe, die Sie tätigen, diese Ausnahmen möglicherweise auslösen könnten, @ExpectedExceptionwird Ihr Test durch die Anmerkung grün markiert, selbst wenn es sich um die falsche Zeile handelt, die die Ausnahme auslöst !

Für diese Situation habe ich eine Klasse geschrieben, die sicher viele andere hier geschrieben haben. Das ist eine assertThrowsMethode:

public class Exceptions {
    private Exceptions(){}

    public static void assertThrows(Class<? extends Exception> expectedException, Runnable actionThatShouldThrow){
        try{
            actionThatShouldThrow.run();
            fail("expected action to throw " + expectedException.getSimpleName() + " but it did not.");
        }
        catch(Exception e){
            if ( ! expectedException.isInstance(e)) {
                throw e;
            }
        }
    }
}

Diese Methode wird einfach zurückgegeben, wenn die Ausnahme ausgelöst wird, sodass Sie in Ihrem Test weitere Zusicherungen / Überprüfungen vornehmen können.

Mit der Java 8-Syntax sieht Ihr Test wirklich gut aus. Im Folgenden finden Sie einen der einfacheren Tests für unser Modell, bei dem die Methode verwendet wird:

@Test
public void when_input_lower_bound_is_greater_than_upper_bound_axis_should_throw_illegal_arg() {
    //setup
    AxisRange range = new AxisRange(0,100);

    //act
    Runnable act = () -> range.setLowerBound(200);

    //assert
    assertThrows(IllegalArgumentException.class, act);
}

Diese Tests sind ein wenig wackelig, weil der Schritt "Akt" eigentlich keine Aktion ausführt, aber ich denke, die Bedeutung ist immer noch ziemlich klar.

Es gibt auch eine winzige kleine Bibliothek auf maven namens catch-exception , die die Syntax im Mockito-Stil verwendet, um zu überprüfen, ob Ausnahmen ausgelöst werden. Es sieht hübsch aus, aber ich bin kein Fan von dynamischen Proxys. Trotzdem ist die Syntax so raffiniert, dass sie verlockend bleibt:

// given: an empty list
List myList = new ArrayList();

// when: we try to get the first element of the list
// then: catch the exception if any is thrown 
catchException(myList).get(1);

// then: we expect an IndexOutOfBoundsException
assert caughtException() instanceof IndexOutOfBoundsException;

Für die Situation, in die ich geraten bin, um zu diesem Thread zu gelangen, gibt es eine Möglichkeit , Tests zu ignorieren , wenn eine Bedingung erfüllt ist.

Im Moment arbeite ich daran, einige DLLs über eine Java-Bibliothek zum Laden nativer Bibliotheken namens JNA aufzurufen, aber unser Build-Server befindet sich in Ubuntu. Ich versuche gerne, diese Art von Entwicklung mit JUnit-Tests voranzutreiben - auch wenn sie zu diesem Zeitpunkt weit von "Einheiten" entfernt sind -. Ich möchte den Test ausführen, wenn ich mich auf einem lokalen Computer befinde, den Test jedoch ignorieren, wenn wir Ubuntu verwenden. JUnit 4 hat hierfür eine Bestimmung namens Assume:

@Test
public void when_asking_JNA_to_load_a_dll() throws URISyntaxException {
    //this line will cause the test to be branded as "ignored" when "isCircleCI" 
    //(the machine running ubuntu is running this test) is true.
    Assume.assumeFalse(BootstrappingUtilities.isCircleCI());
    //an ignored test will typically result in some qualifier being put on the results, 
    //but will also not typically prevent a green-ton most platforms. 

    //setup
    URL url = DLLTestFixture.class.getResource("USERDLL.dll");
    String path = url.toURI().getPath();
    path = path.substring(0, path.lastIndexOf("/"));

    //act
    NativeLibrary.addSearchPath("USERDLL", path);
    Object dll = Native.loadLibrary("USERDLL", NativeCallbacks.EmptyInterface.class);

    //assert
    assertThat(dll).isNotNull();
}
Groostav
quelle
Wie Sie sagten, macht "act" nichts Bestimmtes, so dass wir immer noch nicht wissen, was genau die Ausnahme auslöst, ob es die Linie ist, die wir erwarten oder nicht. In diesem Fall ist das Beispiel recht einfach zu testen und zu verifizieren. Stellen Sie sich jedoch vor, Ihr Test enthält die Utility-Methode, die verschachtelte Methoden verwendet, bevor Sie das Ergebnis zurückgeben. Immer noch keine Möglichkeit, die Ursache des Problems zu ermitteln, es sei denn, IDE gibt die Ausnahmeseite an
Farid
4

Ich suchte auch nach einer passMethode für JUnit, um einige Tests kurzschließen zu können, die in einigen Szenarien nicht anwendbar waren (es gibt Integrationstests anstelle von reinen Komponententests). Schade, dass es nicht da ist.

Glücklicherweise gibt es eine Möglichkeit, einen Test bedingt ignorieren zu lassen, was in meinem Fall sogar noch besser passt, wenn folgende assumeTrueMethode verwendet wird:

Assume.assumeTrue (isTestApplicable);

Hier wird der Test also nur ausgeführt, wenn isTestApplicable wahr ist, andernfalls wird der Test ignoriert.

Sebastian K.
quelle
2

Die Pass-Methode ist nicht erforderlich, da der Unit-Testfall bestanden wird, wenn keine AssertionFailedException aus dem Testcode ausgelöst wird.

Die fail () -Methode löst tatsächlich eine AssertionFailedException aus, um den TestCase nicht zu bestehen, wenn die Steuerung an diesem Punkt angelangt ist.

Ajay
quelle
Ich denke, es ist tatsächlich ein junit.framework.AssertionFailedError.
Kyle
Was ist mit den Fehlerfällen? Sagen wir, ich bekomme eine nicht sichtbare Ausnahme vom Webdriver. Soll ich eine Ausnahme abfangen und zurückkehren?
Sasikumar
1

Ich denke, dass diese Frage das Ergebnis eines kleinen Missverständnisses des Testausführungsprozesses ist. In JUnit (und anderen Testtools) werden die Ergebnisse pro Methode und nicht pro Assert-Aufruf gezählt. Es gibt keinen Zähler, der festhält, wie viele bestanden / nicht bestanden assertXausgeführt wurden.

JUnit führt jede Testmethode separat aus. Wenn die Methode erfolgreich zurückgegeben wird, wird der Test als "bestanden" registriert. Wenn eine Ausnahme auftritt, wird der Test als "fehlgeschlagen" registriert. Im letzteren Fall sind zwei Unterfälle möglich: 1) eine JUnit-Assertionsausnahme, 2) jede andere Art von Ausnahmen. Der Status wird im ersten Fall "fehlgeschlagen" und im zweiten Fall "Fehler" sein.

In der AssertKlasse stehen viele Kurzmethoden zur Verfügung, um Assertionsausnahmen auszulösen. Mit anderen Worten, Assertist eine Abstraktionsschicht über JUnits Ausnahmen.

Dies ist beispielsweise der Quellcode von assertEqualsauf GitHub :

/**
 * Asserts that two Strings are equal.
 */
static public void assertEquals(String message, String expected, String actual) {
    if (expected == null && actual == null) {
        return;
    }
    if (expected != null && expected.equals(actual)) {
        return;
    }
    String cleanMessage = message == null ? "" : message;
    throw new ComparisonFailure(cleanMessage, expected, actual);
}

Wie Sie sehen können, passiert im Falle der Gleichheit nichts, sonst wird eine Ausnahme geworfen.

Damit:

assertEqual("Oh!", "Some string", "Another string!");

ComparisonFailurelöst einfach eine Ausnahme aus, die von JUnit abgefangen wird, und

assertEqual("Oh?", "Same string", "Same string");

tut nichts.

In der Summe pass()würde so etwas keinen Sinn ergeben, weil es nichts getan hat.

Dávid Horváth
quelle