Ich brauche bitte mal kurz einen Sanity/Reality-Check betreffend Tests.

Robert Zenz

Top Contributor
Entweder habe ich ein Verstaendnisproblem oder eine Bildungsluecke (oder beides) was Tests angeht, und ich braeuchte da mal kurz ein paar klaerende Meinungen. Fangen wir mal mit dem zu testenden Code an, absichtlich etwas konvolut und nicht ganz logisch, aber bleibt mal bei mir:

Java:
public class ApiEndpoint {
    protected Backend backend = null;
    
    public ApiEndpoint(
            Backend backend) {
        super();
        
        this.backend = backend;
    }
    
    public Value getValue(String id, String criteria) {
        return backend.getValue(id, criteria);
    }
}

public class Backend {
    protected Storage storage = null;
    
    public Backend(
            Storage storage) {
        super();
        
        this.storage = storage;
    }
    
    public Value getValue(String id, String criteria) {
        // Custom helper only to catch the most glaring mistakes...basically
        // like assertions
        Require.nonNullNorEmpty("id", id);
        Require.nonNull("criteria", criteria);
        
        for (String existingId : storage.listValueIds()) {
            if (existingId.equals(id)) {
                Value value = loadValue(existingId);
                
                if (performSomeComparisonsOrSuch(value, criteria)) {
                    return value;
                }
            }
        }
        
        return null;
    }
}

public class Storage {
    public Storage() {
        super();
    }
    
    public List<String> listValueIds() {
        // Imagine some storage/database/filesystem access here.
        return thatIdList;
    }
    
    public Value loadValue(String id) {
        // Imagine some storage/database/filesystem access here.
        return thatValue;
    }
}

Nein, die Antwort "aber das kann man vereinfachen" ist nicht gewollt. Das ist Absicht so.

So...jetzt habe ich festgestellt dass es Leute gibt welche fuer den Endpunkt dann solche Unit-Tests schreiben:

Java:
@Test
public void testGetValue() {
    Mockito.doReturn(new Value())
            .when(backend)
            .getValue(Mockito.any(), Mockito.any());
    
    Assertions.assertNotNull(apiEndpoint.getValue("ID", "vegetables");
    
    Mockito.verify(backend)
            .getValue(Mockito.any(), Mockito.any());
}

Und hier faengt jetzt meine eigentlich Frage an...mit dem habe ich doch nichts getestet, oder?! Der Test ist doch komplett wertfrei, da koennte ich genauso gut ein Code-Diff zur vorherigen Revision machen. Oder bin ich blind und sehe den Wert nicht?!

Ein Test wie ich ihn in dem Fall machen wuerde waere folgendes:

Java:
// @ExtendWith(MockitoExtension) to catch all mocked but not invoked functions.
// All Mocks created with a failing/throwing default answer to catch non-mocked
// calls.

@Test
public void testGetValue() {
    Mockito.doReturn(Arrays.asList("value1", "value2", "value3"))
            .when(storage)
            .listValueIds();
    
    Mockito.doReturn(new Value("ImagineASaneConstructorHere"))
            .when(storage)
            .loadValue("value2");
    
    Value returnedValue = apiEndpoint.getValue("value2", "thatCriteria");
    
    Assertions.assertIsThatValueWeWanted(returnedValue);
}

Damit habe ich das Prinzip von "es sollte nur eine Einheit getestet werden" natuerlich gebrochen, aber nur so kann ich, meiner Meinung nach, garantieren dass die Funktion auch das tut was ich von ihr erwarte. Eigentlich ist mir sogar der Storage-Mock ein Dorn im Auge, da waere mir ein Storage welches auf definierte Test-Daten geht sogar noch lieber (aber das ist nicht immer so gut moeglich). Jetzt sagt man "dass ist kein Unit-Test, das ist ein Integrations-Test", gut, wird so sein, aber nur mit einem solchen Test kann ich meiner Meinung nach eine brauchbare Aussage ueber die Funktionstuechtigkeit dieser Methode machen.

Ein sehr extremes Beispiel dafuer ist eine HTTP-API welche mit Spring umgesetzt wurde, nehmen wir mal an dass ApiEndpoint ein Controller ist:

Java:
@Controller
public class ApiEndpoint {
    protected Backend backend = null;
    
    public ApiEndpoint(
            Backend backend) {
        super();
        
        this.backend = backend;
    }
    
    @GetMapping("/module/v1/stuff/value/{ID}")
    public Value getValue(
            @PathVariable("ID") String id,
            @RequestParam("criteria") String criteria) {
        return backend.getValue(id, criteria);
    }
}

Wenn ich dann noch so einen Unit-Test (der erste oben) habe wo alles gemockt ist, und ich weder ein richtiges Backend noch ein Spring starte, kann ich doch gar nichts dazu sagen ob die Funktion auch so funktioniert wie ich erwarte oder nicht bis ich damit auf dem Test-System war. Das ist doch viel zu spaet, oder?

Also was mir gesagt wurde war "Spring starten ist ein Integrations-Tests, Integrations-Tests machen wir nur am Test-System", dass dann die erste Idee war dass wir fuer das Test-System auch nur volle Release-Pakete verwenden duerfen (ein halber Tag um diese zu bauen und mindestens zwei Personen) konnte ich ja noch erfoglreich abwehren. Aber dennoch, ich will doch wissen ob das Ding funktioniert, am besten in dem Moment wo ich meine Aenderungen zur Durchsicht uebergebe (Merge Request mit Pipeline) und nicht erst wenn ich diese auf irgendeinem Test-System (vielleicht noch haendisch) getestet habe. Das ist doch viel zu spaet und zerbrechlich, oder?

Ich verstehe die Argumentation mit den Mocks, ja. Aber ich will doch Black-Box Tests haben in erster Linie, also nicht dass ich Methoden-Aufrufe abfrage sondern dass ich Verhalten validiere.

Der klare Nachteil an meinen Test-Ansatz ist dass wenn Storage oder Backend zerbrochen ist, wird so ziemlich alles Rot. Der Vorteil ist dass ich sehe was dann alles zerbricht, hehe, kleiner Scherz.

Ein weiteres Thema in der Hinsicht ist das Pruefen von Ausnahmen. Beispiel von oben, ich will validieren dass eine criteria von null eine entsprechende Ausnahme erzeugt. Der Ansatz der mir gesagt wurde ist:

Java:
@Test
public void testGetValueWithNullCriteria() {
    IllegalArgumentException thrownException = Assertions.assertThrows(IllegalArgumentException.class, () -> backend.getValue("valid", null));
    Assertions.assertEquals("Parameter \"criteria\" cannot be <null>.", thrownException.getMessage());
}

Damit bekomme ich die Meldung dass der erwartete String nicht stimmte, zum Beispiel dann sowas wie:

Assertion failed, expected <Parameter "criteria" cannot be <null>.> but was <null>.

Gut, damit weisz ich dann dass es nicht funktioniert, aber das ist mir viel zu wenig Information, insbesondere wo ich die Informationen haette. Mein Ansatz war daher immer dieser:

Java:
@Test
public void testGetValueWithNullCriteria() {
    try {
        backend.getValue("valid", null));
        
        Assertions.fail("Call should have failed because of an illegal argument, but did not.");
    } catch (IllegalArgumentException e) {
        if (!Object.equals(e.getMessage(), "Parameter \"criteria\" cannot be <null>.") {
            throw e;
        }
    }
}

Ja, das liest sich wie ein Backstein (koennte man vereinfachen mit einer Hilfsfunktion), aber so habe ich im Falle dass der Test versagt direkt alle Informationen verfuegbar. Jetzt wurde mir gesagt "Wenn du wissen willst wo die unerwartete Ausnahme herkam, verwende einen Debugger, dass ist nicht die Aufgabe eines Tests." und bei der Aussage bin ich einfach wie der Ochse vorm Scheunentor ausgestiegen. Will man nicht so viele Informationen wie moeglich mit dem Versagen des Tests bekommen? Ich muss doch auf das Ergebnis des Testlaufs blicken und direkt sagen koennen was falsch ist, oder?

Oder bin ich hier komplett neben der Spur und Realitaet und einfach nicht genug gebildeter Programmierer?
 

KonradN

Super-Moderator
Mitarbeiter
Damit habe ich das Prinzip von "es sollte nur eine Einheit getestet werden" natuerlich gebrochen, aber nur so kann ich, meiner Meinung nach, garantieren dass die Funktion auch das tut was ich von ihr erwarte.
Ja, aber das ist doch der Kernpunkt. Alles, was Du bei der Methode
Java:
    public Value getValue(String id, String criteria) {
        return backend.getValue(id, criteria);
    }
testen kannst / musst, ist doch, dass bei einem Aufruf von getValue des Enpoints genau diese Methode des Backends aufgerufen wird und dass das Ergebnis zurück gegeben wird.

Daher ist der Test, den Du da gegeben hast, auch nicht korrekt. Der Test würde einen Fehler nicht finden:
Java:
    public Value getValue(String id, String criteria) {
        backend.getValue(id, criteria);
        return new Value("whatever");
    }

Oder ich habe auf die Schnelle bei dem Unit Test etwas übersehen. Ich hätte halt in dem Test:
  • erst id, criteria und Value erzeugt in Variablen. Beim Mocking würde ich sagen, dass das Backend bei dem Aufruf mit id und criteria eben das erzeugte Value zurückgeben soll.
  • dann kommt der Aufruf mit dem Speichern des Ergebnisses
  • Bei den Asserts kommt dann, dass gespeichter value und Rückgabe gleich sein müssen und dass das Mocking einmal aufgerufen werden musste. Letzteres ist weniger wichtig. Wäre der Aufruf nicht benutzt worden, dann würde Mockito einen Fehler melden. Und ist die Anforderung nicht erfüllt, wenn ich folgenden Code hätte:
Java:
    public Value getValue(String id, String criteria) {
        backend.getValue(id, criteria);
        return backend.getValue(id, criteria);
    }
Also nicht missverstehen: Das ist nicht ok und sollte behoben werden. Aber die Spezifikation enthält diese Vorgabe nicht explizit und daher kann man ggf. sagen, dass dies nicht im Test notwendig ist. Ich nehme sowas in der Regel aber mit auf.

Edit: Typo entfernt.
 

Robert Zenz

Top Contributor
Ja, aber das ist doch der Kernpunkt. Alles, was Du bei der Methode
Java:
Code:
    public Value getValue(String id, String criteria) {
        return backend.getValue(id, criteria);
    }
testen kannst / musst, ist doch, dass bei einem Aufruf von getValue des Enpoints genau diese Methode des Backends aufgerufen wird und dass das Ergebnis zurück gegeben wird.
Das ist ein schoenes Argument, das muss ich mir einen Moment durch den Kopf gehen lassen.

Daher ist der Test, den Du da gegeben hast, auch nicht korrekt.
Ist richtig, korrekter waere so etwas:

Java:
// @ExtendWith(MockitoExtension) to catch all mocked but not invoked functions.
// All Mocks created with a failing/throwing default answer to catch non-mocked
// calls.

@Test
public void testGetValue() {
    Mockito.doReturn(Arrays.asList("value1", "value2", "value3"))
            .when(storage)
            .listValueIds();
    
    Value storedValue = new Value("ImagineASaneConstructorHere");
   
    Mockito.doReturn(storedValue)
            .when(storage)
            .loadValue("value2");
    
    Value returnedValue = apiEndpoint.getValue("value2", "thatCriteria");
    
    Assertions.assertSame(storedValue, returnedValue);
}

Also nicht missverstehen: Das ist nicht ok und sollte behoben werden. Aber die Spezifikation enthält diese Vorgabe nicht explizit und daher kann man ggf. sagen, dass dies nicht im Test notwendig ist. Ich nehme sowas in der Regel aber mit auf.
Nein, das ist schon richtig.

Eventuell ist hier meine Diskrepanz auch irgendwas zwischen Test-Philosophien und dem was ich gerne testen wuerde, oder so.
 

KonradN

Super-Moderator
Mitarbeiter
Bezüglich meiner Kritik an dem Test bezog es sich auf den ersten Test, der eigentlich nichts macht. Die Variante, die eine gewisse Funktionalität mit testet, hatte ich im Detail nicht angesehen.

Eventuell ist hier meine Diskrepanz auch irgendwas zwischen Test-Philosophien und dem was ich gerne testen wuerde, oder so.

Das Problem kann ich prinzipiell nachvollziehen. Man will halt gerne sinnvolle Tests schreiben. Da ist ein Unit Test für etwas, das eigentlich nichts macht (außer einen Service aufzurufen oder so) wirklich sehr trivial und testet sehr wenig. Das ist eine Thematik, ich sehr oft sehe bei den Tests von Controllern. Da Controller ja eigentlich keine Logik haben hast Du da oft wirklich reine Tests, die schauen: Wird der Service, der da zu Grunde liegt, wirklich korrekt aufgerufen?

Gerade am Anfang, wenn man mehr mit Regression oder Integration Tests gearbeitet hat, dann fällt sowas sehr schwer. Ich habe am Anfang auch immer gerne Unit Tests geschrieben, die dann nicht einzelne Dinge getestet haben sondern mehr ganze Funktionalitäten.

Also ich habe etwas geschrieben, das eine Entity verwalten kann. Dann gab es einen Test, der dann halt:
  • Datenbank initialisiert / geprüft hat
  • Dann wurde geprüft: TestEntity lesen -> nicht gefunden
  • TestEntity anlegen
  • TestEntity lesen -> Wurde sie richtig gelesen?
  • TestEntity verändern und speichern -> test durch erneutes Lesen und Vergleich
  • TestEntity löschen -> Ist die TestEntity danach nicht mehr lesbar?

Das warn Tests, die dann auch eine gute Abdeckungen hatten. Aber die Anforderungen waren dann immer sehr hoch. Unsere Entwicklungsrechner hatten also sehr hohe Anforderungen bezüglich Konfiguration und so. Datenbanken und so mussten zur Verfügung stehen. Teilweise hatten wir Drittsysteme, die bei einzelnen Tests notwendig waren (also man musste im Firmennetz sein und die Testsysteme mussten laufen) und all sowas ...
Und dann wurde halt nur der erste Fehler gefunden. Und der musste dann erst lokalisiert werden, weil ja viele Klassen genutzt wurden (was dann den Debugger erforderte) und nach der Behebung kam halt dann der zweite Fehler u.s.w.

Beim Umdenken hat mit TDD geholfen. Da wird halt wirklich nur getestet, was du gerade als Anforderung baust. Wenn die Anforderung a.la. "wenn der Parameter null ist, dann wirf eine Exception" nicht da ist, dann wird die auch nicht "gebaut" und damit auch nicht getestet. (Wobei da die Reglung sein sollte, dass diese Anforderungen impliziert sind bei jeder API! Dann mache ich das in der Regel als erste Funktionalität und als erste Tests!)
Und das hilft dann auch bei Dir:
Du sollst den Controller samt Tests schreiben, aber der Service wird von einem Kollegen geschrieben. Was der genau macht und die Implementation sind unbekannt. Den Task könntest Du Dir zwar ansehen mit Anforderung und Akzeptanzkriterien aber es ist nicht deine Baustelle. Du weisst aber: Das ist die Klasse Backend und da gibt es dann eine bestimmte Methode. Die Klasse kannst Du bei Dir auch schon anlegen a.la.:
Java:
public class Backend {
    public Value getValue(String id, String criteria) {
        throw new NotImplementedException();
    }
}
Und nun schreib den Test. Dein Controller sollte testbar sein (erfolgreich), auch wenn Backend einen Fehler hat.

Das hilft evtl. auch als Gedanke, wenn man die Dinge parallel schreibt. Das gleiche trifft auf Backend zu: Der Entwickler, der Backend schreibt will sein Backend erfolgreich testen während parallel jemand anderes Storage schreibt.

Das nur so als meine Sicht - vielleicht hilft es Dir oder Anderen, die über diesen Thread stolpern.
 

mrBrown

Super-Moderator
Mitarbeiter
Ein weiteres Thema in der Hinsicht ist das Pruefen von Ausnahmen. Beispiel von oben, ich will validieren dass eine criteria von null eine entsprechende Ausnahme erzeugt. Der Ansatz der mir gesagt wurde ist:

Java:
@Test
public void testGetValueWithNullCriteria() {
IllegalArgumentException thrownException = Assertions.assertThrows(IllegalArgumentException.class, () -> backend.getValue("valid", null));
Assertions.assertEquals("Parameter \"criteria\" cannot be <null>.", thrownException.getMessage());
}
Damit bekomme ich die Meldung dass der erwartete String nicht stimmte, zum Beispiel dann sowas wie:

Assertion failed, expected <Parameter "criteria" cannot be <null>.> but was <null>.

Gut, damit weisz ich dann dass es nicht funktioniert, aber das ist mir viel zu wenig Information, insbesondere wo ich die Informationen haette. Mein Ansatz war daher immer dieser:

Java:
@Test
public void testGetValueWithNullCriteria() {
try {
backend.getValue("valid", null));

Assertions.fail("Call should have failed because of an illegal argument, but did not.");
} catch (IllegalArgumentException e) {
if (!Object.equals(e.getMessage(), "Parameter \"criteria\" cannot be <null>.") {
throw e;
}
}
}
Ja, das liest sich wie ein Backstein (koennte man vereinfachen mit einer Hilfsfunktion), aber so habe ich im Falle dass der Test versagt direkt alle Informationen verfuegbar. Jetzt wurde mir gesagt "Wenn du wissen willst wo die unerwartete Ausnahme herkam, verwende einen Debugger, dass ist nicht die Aufgabe eines Tests." und bei der Aussage bin ich einfach wie der Ochse vorm Scheunentor ausgestiegen. Will man nicht so viele Informationen wie moeglich mit dem Versagen des Tests bekommen? Ich muss doch auf das Ergebnis des Testlaufs blicken und direkt sagen koennen was falsch ist, oder?

Die erste Variante sollte da eigentlich immer mehr Infos geben als die zweite, zumindest wenn man eine sinnvolle Assertions-Lib benutzt (ich persönlich kenne nur AssertJ, aber andere machen das sicher ähnlich).

Gibt ja im wesentlichen drei Fälle die man betrachten muss
  1. korrekte Exception mit korrekter Message wird geworfen
    • beide sollten da einen erfolgreichen Test zeigen
  2. korrekte Exception mit falscher Message wird geworfen
    • Variante 1 schlägt fehl, mit der Info dass die Message nicht richtig ist, und liefert dazu als Kontext die originale Exception, wie sie geloggt werden würde (Test schlägt aber wegen der Assertions fehl, nicht wegen der Exception).
    • Variante 2 schlägt fehl, da die originale Exception geworfen wird
  3. falsche Exception
    • Variante 1 schlägt fehl mit der info, dass es die falsche Exception ist und liefert wieder das Original als Kontext
    • Variante 2 schlägt mit eigener Nachricht fehl, aber ohne Kontext
Mit Variante 2 hat man da niemals mehr Infos, sondern nur weniger. Könnte man natürlich alles dazu bauen, aber am Ende gibt man dann mit viel eigenem Code genau das gleiche aus wie Variante 1.
 

mrBrown

Super-Moderator
Mitarbeiter
Wenn ich dann noch so einen Unit-Test (der erste oben) habe wo alles gemockt ist, und ich weder ein richtiges Backend noch ein Spring starte, kann ich doch gar nichts dazu sagen ob die Funktion auch so funktioniert wie ich erwarte oder nicht bis ich damit auf dem Test-System war. Das ist doch viel zu spaet, oder?

Also was mir gesagt wurde war "Spring starten ist ein Integrations-Tests, Integrations-Tests machen wir nur am Test-System", dass dann die erste Idee war dass wir fuer das Test-System auch nur volle Release-Pakete verwenden duerfen (ein halber Tag um diese zu bauen und mindestens zwei Personen) konnte ich ja noch erfoglreich abwehren. Aber dennoch, ich will doch wissen ob das Ding funktioniert, am besten in dem Moment wo ich meine Aenderungen zur Durchsicht uebergebe (Merge Request mit Pipeline) und nicht erst wenn ich diese auf irgendeinem Test-System (vielleicht noch haendisch) getestet habe. Das ist doch viel zu spaet und zerbrechlich, oder?
Grad bei Spring (Boot) würde ich die Grenzen zwischen Unit-/Modul-/Integration-/System-Test verschwimmen lassen bzw ignorieren.

Da lässt sich völlig problemlos die Anwendung "scheibchenweise" starten, also z.B. um den Controller zu testen nur der dafür relevante Teil, und das ganze dahinter inklusive aller Services kann man wegmocken. All das, was Spring da durch Annotationen am Controller gesteuert macht kann man dann super testen, also werden Parameter und Body aus dem Request richtig deserialisiert, greift Validierung, ist überhaupt das Mapping richtig.
Dabei darauf zu pochen, dass es ja ein Integration-Test ist und der ja nur da und dort unter den Bedingungen ausgeführt werden darf ist meiner Meinung nach Unsinn, damit verschenkt man super viel.
 

KonradN

Super-Moderator
Mitarbeiter
Dabei darauf zu pochen, dass es ja ein Integration-Test ist und der ja nur da und dort unter den Bedingungen ausgeführt werden darf ist meiner Meinung nach Unsinn, damit verschenkt man super viel.
Wie sieht es denn dann konkret aus?

Nehmen wir einen einfachen Fall: Du hast Einen Controller und der greift auf einen Service zu. Machen wir es so wie hier mit nur einer Methode.

Dann hast Du nur einen Test beim Controller, der diese Methode testet und der Service wird mit getestet und daher hat der keinen eigenen Test?

Oder hast Du zwei mal den gleichen Test? Einmal beim Controller und dann einmal beim Service?

Ich habe bei beidem meine Probleme. Einfach mal ein paar Szenarien:
  • Mehrere überschneidende Tests. Dann hast Du eine Klasse, die an vielen Stellen mit getestet wird. Wenn sich da nun etwas ändert, dann sind ganz viele Tests anzupassen. Ist da ein Fehler, dann schlagen plötzlich ganz viele Test fehl.
  • Keine überschneidenden Tests: Der Controller wird evtl. nicht mehr gebraucht. Dann fliegt der Controller mit seinen Tests raus. Der Service bleibt ab. Und schon hat man produktiven Code ohne Test.

Oder habe ich da jetzt irgend etwas übersehen?
 

mrBrown

Super-Moderator
Mitarbeiter
Oder habe ich da jetzt irgend etwas übersehen?
Den Teil mit "das ganze dahinter inklusive aller Services kann man wegmocken" und den expliziten Bezug auf diesen Teil (und dabei insbesondere "Spring starten ist ein Integrations-Tests, Integrations-Tests machen wir nur am Test-System")?
Also was mir gesagt wurde war "Spring starten ist ein Integrations-Tests, Integrations-Tests machen wir nur am Test-System", dass dann die erste Idee war dass wir fuer das Test-System auch nur volle Release-Pakete verwenden duerfen (ein halber Tag um diese zu bauen und mindestens zwei Personen) konnte ich ja noch erfoglreich abwehren. Aber dennoch, ich will doch wissen ob das Ding funktioniert, am besten in dem Moment wo ich meine Aenderungen zur Durchsicht uebergebe (Merge Request mit Pipeline) und nicht erst wenn ich diese auf irgendeinem Test-System (vielleicht noch haendisch) getestet habe. Das ist doch viel zu spaet und zerbrechlich, oder?

Ich hätte genau wie Du zwei getrennte Tests für Service und Controller, für letzteren gäb's mit Spring Boot aber oft einen WebMvcTest. Ist kein Unit-Test mehr, aber von Komplexität des Test-Codes und der Ausführungszeiten nicht weit davon weg, und wenn man ehrlich ist sind die beiden Dinge mit am Entscheidendsten für Anzahl und Sinnhaftigkeit von Tests
 

Robert Zenz

Top Contributor
@KonradN Ja, genau so wie du es beschrieben hast. Am liebsten waeren mir auch "Blackbox-Tests" wo ich even nur Eingabe/Rueckgabe/Zustand dann pruefe. Die Aufrufe die da passieren in den Methoden verstehe ich eben mehr als Implementierungsdetail welches ich eher nicht testen wuerde. Aber ich verstehe deine Erklaerung sehr gut, dass macht es schon viel deutlicher im Kopf wie das mit den Mocks genau gedacht ist und wie die Philosophie dahinter ist. Danke.

Die erste Variante sollte da eigentlich immer mehr Infos geben als die zweite, zumindest wenn man eine sinnvolle Assertions-Lib benutzt (ich persönlich kenne nur AssertJ, aber andere machen das sicher ähnlich).
Das ist bei JUnit eben leider nicht der Fall, weil man eben die Informationen der originalen Ausnahme verliert. Ich muss mir irgendwann ein Hilfs-Teil mit einer eigenen Assert-Methode schreiben die das besser wegkapselt.

Grad bei Spring (Boot) würde ich die Grenzen zwischen Unit-/Modul-/Integration-/System-Test verschwimmen lassen bzw ignorieren.

Da lässt sich völlig problemlos die Anwendung "scheibchenweise" starten, also z.B. um den Controller zu testen nur der dafür relevante Teil, und das ganze dahinter inklusive aller Services kann man wegmocken. All das, was Spring da durch Annotationen am Controller gesteuert macht kann man dann super testen, also werden Parameter und Body aus dem Request richtig deserialisiert, greift Validierung, ist überhaupt das Mapping richtig.
Dabei darauf zu pochen, dass es ja ein Integration-Test ist und der ja nur da und dort unter den Bedingungen ausgeführt werden darf ist meiner Meinung nach Unsinn, damit verschenkt man super viel.
Okay, dann bin ich da nicht neben der Spur mit der Meinung, beruhigt mich. Danke.
 

mrBrown

Super-Moderator
Mitarbeiter
Das ist bei JUnit eben leider nicht der Fall, weil man eben die Informationen der originalen Ausnahme verliert. Ich muss mir irgendwann ein Hilfs-Teil mit einer eigenen Assert-Methode schreiben die das besser wegkapselt.

Java:
Assertions.assertThrows(IllegalAccessError.class, ()-> { throw new IllegalArgumentException("foo");});
liefert direkt mit Junit diese Ausgabe:

Code:
org.opentest4j.AssertionFailedError: Unexpected exception type thrown ==> expected: <java.lang.IllegalAccessError> but was: <java.lang.IllegalArgumentException>

    ...
    at bar.ExceptionTest.name(ExceptionTest.java:10)
Caused by: java.lang.IllegalArgumentException: foo
    at bar.ExceptionTest.lambda$name$0(ExceptionTest.java:10)
    at org.junit.jupiter.api.AssertThrows.assertThrows(AssertThrows.java:55)
    ... 72 more


Und alternativ mit AssertJ:
Java:
Assertions.assertThatThrownBy(()-> { throw new IllegalArgumentException("foo");})
            .isInstanceOf(IllegalAccessError.class);

gibts dieses:
Code:
java.lang.AssertionError:
Expecting actual throwable to be an instance of:
  java.lang.IllegalAccessError
but was:
  java.lang.IllegalArgumentException: foo
    at bar.ExceptionTest.lambda$assertj$1(ExceptionTest.java:15)
    at org.assertj.core.api.ThrowableAssert.catchThrowable(ThrowableAssert.java:63)

Was fehlt dir da jeweils?

Bisher hatte ich nämlich immer den gegenteiligen Fall zu dir: jemand hat das Testen der Exceptions händisch gemacht, und das ganze entweder fehlerhaft oder mit viel weniger Infos als mit den Standard-Assertions gelöst. Mit deiner Variante zB würde der Test einfach mit der Originalen Exception fehlschlagen, das liefert doch deutlich weniger Infos, als das was die Assertions da liefern?
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
Momo16 Brauche Hilfe - Java Projekt kann nicht erstellt werden Allgemeine Java-Themen 12
_user_q Was brauche ich, um eine eigene "Search for updates"-Funktion einzubauen? Allgemeine Java-Themen 1
MoxxiManagarm Ich brauche einen smarten Ansatz Allgemeine Java-Themen 23
RalleYTN Brauche Hilfe beim Run-Length-Decoding Allgemeine Java-Themen 9
J Java "Bank Programm" Brauche eure Hilfe Allgemeine Java-Themen 3
Thallius Unfassbarer Fehler. Brauche Ideen zum Debuggen Allgemeine Java-Themen 9
J Unvorhersehbares Verhalten - benutze ich die falsche Bedingungsprüfung oder brauche ich Threads? Allgemeine Java-Themen 12
H NetBeans geotools 15 oder 16 - welche Abhängingkeiten gibt es? Welche Jars brauche ich? Allgemeine Java-Themen 4
S Best Practice Brauche eine Idee für eine Java Projekt! Allgemeine Java-Themen 11
M Brauche Hilfe bei Quellcodeverständnis Allgemeine Java-Themen 4
D Brauche Hilfe bei Rechnung (Exponentiation) Allgemeine Java-Themen 12
F Brauche Hilfe bei Java Allgemeine Java-Themen 6
C Brauche Hilfe mit Modulo Strategie Allgemeine Java-Themen 2
S Brauche Hilfe: Prüfungsaufgabe Java Allgemeine Java-Themen 8
Fu3L Input/Output Brauche Rat bei effizienter Speicherung großer Datenmengen Allgemeine Java-Themen 21
S brauche Hilfe für ssh connection in java Allgemeine Java-Themen 6
T Ich brauche gute Quellen Allgemeine Java-Themen 13
S Brauche Hilfe bei Planung für JAVA-Programm Allgemeine Java-Themen 3
hdi GUI: Ich brauche ein Textfeld mit "Sperre" Allgemeine Java-Themen 12
Developer_X Brauche Hilfe bei meinem kleinen hilfsprogramm Allgemeine Java-Themen 4
Developer_X Bound Animation - Brauche Hilfe Allgemeine Java-Themen 3
E brauche schnell Ausführbare Datei Allgemeine Java-Themen 4
D Brauche mal Hilfe bin noch nicht so fit Allgemeine Java-Themen 3
M brauche grösseren Datentyp Allgemeine Java-Themen 5
L Brauche Hilfe bei Memory Programmierung Allgemeine Java-Themen 2
N Brauche dringend Hilfe! Allgemeine Java-Themen 7
S brauche dringend help bei einem projekt (Römisch-dezimal) Allgemeine Java-Themen 3
M Brauche einen Tipp, bei einer Aufgabe ! Allgemeine Java-Themen 3
M brauche hilfe mit iReport Allgemeine Java-Themen 4
P Java Acces Datenbank Problem ! (Brauche eine Abfrage) Allgemeine Java-Themen 5
E Ich brauche euren Code Allgemeine Java-Themen 12
V Brauche dringend Hilfe. Object-handling Allgemeine Java-Themen 4
S Ich Brauche Bei Einen Bot Hile :( Allgemeine Java-Themen 2
V Brauche Beratung in Sachen Java+Datenbank+Web Allgemeine Java-Themen 8
M Schätzfrage: Wieviel Prozessorpower brauche ich? Allgemeine Java-Themen 27
R Hilfe - brauche ein Applet zu Demozwecken Allgemeine Java-Themen 10
J Brauche Hilfe zum ToolKit Allgemeine Java-Themen 15
K Brauche Hilfe bei Simulation einer Flugkurve . Allgemeine Java-Themen 14
T Brauche Hilfe! Allgemeine Java-Themen 10
T Lizenzfrage - brauche Infos Allgemeine Java-Themen 34
T Autostart Programm - brauche Tipps Allgemeine Java-Themen 5
G Brauche hilfe bei JMF: Wie eigene Streaming Data Source! Allgemeine Java-Themen 4
N int[] referenzen in ein Array packen, brauche Hilfe. Allgemeine Java-Themen 7
A brauche schnelle antwort wg. schleife! Allgemeine Java-Themen 3
T Brauche Hilfe bei ClassLoader Allgemeine Java-Themen 2
K Brauche euren Lösungsweg zu einem File/IO-Beispiel Allgemeine Java-Themen 23
G Brauche Hilfe bei Erstellung eines Musters! Allgemeine Java-Themen 3
G packageproblem? brauche rat! Allgemeine Java-Themen 2
S brauche Orientierungshilfe in Java Allgemeine Java-Themen 5
Z Was brauche ich für Servlets? Allgemeine Java-Themen 17
S Static + Speicher + Bytecode etc. Brauche HILFE :/ Allgemeine Java-Themen 11
Zrebna Bitte um Empfehlungen für "zeitlose" Bücher bzgl. Backend mit Spring und Beans Allgemeine Java-Themen 25
jhCDtGVjcZGcfzug Klassen Was genau passiert hier? Kann mir das jemand bitte Zeile für Zeile erklären? Allgemeine Java-Themen 1
B Bitte um Code Erklärung Allgemeine Java-Themen 5
L Einfache Navigations-App schnell selber Programmieren? Bitte um Ideen und Anregungen. Allgemeine Java-Themen 17
J Simple Date Format Alternativen bitte um Code Allgemeine Java-Themen 14
V Bilder bei bestimmten Zeiten wechseln, bitte um Hilfe Allgemeine Java-Themen 5
M Bitte Hilfe mit REGEX (Negieren) Allgemeine Java-Themen 4
I Bitte Drngend hilfe Allgemeine Java-Themen 2
F Socket NullPointerException Bitte um hilfe! Allgemeine Java-Themen 12
M Input/Output Java + OCR ? Erfahrungen und bitte um Hilfe Allgemeine Java-Themen 12
F Bitte um Hilfe! Allgemeine Java-Themen 5
D Code bitte mit 19 stelligen Zahlen kompatibel machen Allgemeine Java-Themen 5
J Probleme bei kompilieren ... Bitte um Hilfe Allgemeine Java-Themen 2
Jats ArrayList-Error Index: 1 Size:1 - Bitte um Hilfe Allgemeine Java-Themen 36
S Bitte um Hilfe Allgemeine Java-Themen 8
N "Bitte warten"-Meldung Allgemeine Java-Themen 6
I Studium - bitte dringend helfen !!! Allgemeine Java-Themen 17
C Eclipse spinnt - bitte Helfen! Allgemeine Java-Themen 3
J Probiert mal bitte aus (Thread + Runtime) Allgemeine Java-Themen 10
L Java6 update N bekommt neues Browser-Plugin, bitte testen. Allgemeine Java-Themen 7
J Bitte um Kurzinfo bez. JMF (ja, war bei Forumssuche/google) Allgemeine Java-Themen 2
E Bitte um Rat: Sortieren mit ArrayList Allgemeine Java-Themen 2
P Bitte um Hilfe Allgemeine Java-Themen 4
K Hab Sch***e gebaut - Bitte um Hilfe Allgemeine Java-Themen 7
G Zertifikat importieren? Bitte HILFE! Allgemeine Java-Themen 2
J synchronized (bitte beantworten, urgent! danke) Allgemeine Java-Themen 11
I Weiß nicht mehr weiter! Bitte helft mir! Allgemeine Java-Themen 5
P Was ist denn Bitte falsch? Allgemeine Java-Themen 2
M Programmierstill: Bitte testen anhand HTML-Tool Allgemeine Java-Themen 18
M Bitte Testen: Mein Multi-File Editor Allgemeine Java-Themen 30
P Bitte kritisieren: rekursive Sortier-Methode Allgemeine Java-Themen 2
J könnt Ihr mir bitte mal den Fehler erläutern! Allgemeine Java-Themen 5
D Große Klasse - was fällt euch so ins Auge? Kritik bitte! Allgemeine Java-Themen 10
thE_29 Vor dem ersten Posten bitte lesen! Allgemeine Java-Themen 0
K bitte um hilfe bei file import Allgemeine Java-Themen 7
N Compiler (javac) funktioniert nicht... Bitte hilfe! Allgemeine Java-Themen 3
B APi methode kurz anhalten Allgemeine Java-Themen 8
KaffeeFan Methoden Suche Methode um Programm kurz warten zu lassen Allgemeine Java-Themen 22
L Garbage Collector lässt Programm kurz hängen Allgemeine Java-Themen 10
L Thread kurz unterbrechen und wieder starten Allgemeine Java-Themen 5
D Problem mit Tooltips und JFrame (Tooltips sind zu kurz!) Allgemeine Java-Themen 4
S Programm kurz anhalten Allgemeine Java-Themen 3

Ähnliche Java Themen


Oben