CodeGym /Kurse /Spring Test /Decision Tree für Boot-Tests

Decision Tree für Boot-Tests

Spring Test
Level 6, Lektion 4
Verfügbar

1. Decision Tree gegen den @SpringBootTest-Reflex

Wir haben die Boot-Tests bereits nach Grenzen aufgeschlüsselt: JSON, MVC, JPA, Outbound-Client und voller Kontext. Aber die Karte allein beantwortet noch nicht die praktische Frage: Welchen Weg wähle ich für einen bestimmten Defekt, ohne reflexartig zu @SpringBootTest zu greifen.

Wenn jemand zum ersten Mal Spring Boot Testing sieht, kommt ihm ganz natürlich der Gedanke: „Wenn @SpringBootTest die gesamte Anwendung hochfährt, dann ist das wohl die zuverlässigste Methode. Ich werde es überall einsetzen – dann habe ich meine Ruhe.“ Genau in diesem Moment verwandelt sich die Teststrategie in eine Religion: „Ich glaube an den vollen Kontext“ statt „Ich wähle das Werkzeug für das Risiko“.

Das Problem ist nicht, dass @SpringBootTest schlecht ist. Das Problem ist, dass es teuer und weitwinkelig ist. Es prüft viele Verknüpfungen auf einmal, aber dafür bezahlt man mit Startzeit, erschwerter Isolierung von Fehlerursachen und einer Neigung zu Redundanz. Die Tests beginnen „alles zu berühren“, und am Ende wird unklar, was genau man nachgewiesen hat: eine Geschäftsregel, einen JSON-Vertrag, das Wiring, das Repository-Verhalten, Einstellungen, Filter, Serialisierung oder nur die Tatsache, dass die Erde sich immer noch um die Sonne dreht.

Der Decision Tree ist ein Weg, diesen Reflex zu stoppen. Er zwingt einen, nicht mit der Annotation zu beginnen, sondern mit der Formulierung des Problems. Und dann Schritt für Schritt kurze Fragen auf der Ebene zu stellen: „Brauche ich Spring überhaupt?“ und „Welche Systemgrenze steht gerade im Verdacht?“. Am Ende spürt man fast körperlich, wo man wirklich den vollen Kontext braucht und wo man zufällig dafür bezahlt wie für ein „All Inclusive“-Abonnement, obwohl man nur stilles Wasser möchte.

2. Schritt 0: Risiko und Schicht formulieren

Bevor man eine Annotation wählt, muss man lernen, den Defekt oder das Risiko in Begriffen der Schicht zu formulieren. Das klingt abstrakt, ist aber in der Praxis eine sehr konkrete Disziplin: Man hört auf zu sagen „etwas ist kaputt“ und beginnt zu sagen „die Grenze X ist kaputt, also werde ich sie mit diesen Mitteln prüfen“. Im Testen ist das fast eine Superkraft.

Eine gute Risikoformulierung beantwortet drei Fragen: Was beobachtet der Client (oder eine andere Schicht), was könnte schiefgehen und wo lebt es? Zum Beispiel: „Der Endpoint GET /api/public/articles/{slug} gibt das Feld publishedAt im falschen Format zurück“ – das ist kein „Controller-Problem“ und kein „Datenbank-Problem“, sondern ein Problem des JSON-Antwortvertrags. Und „Das Repository filtert Artikel nicht mehr nach Status PUBLISHED“ – das ist kein HTTP-Problem, sondern ein Query/Persistence-Boundary-Problem. Und schließlich „Beim Einreichen zur Überprüfung geht der Artikel in den falschen Status über“ – das ist entweder reine Geschäftslogik (Unit) oder ein durchgehendes Wiring mehrerer Schichten (Full Context), und hier muss man präzisieren.

Am Beispiel von ContentHub ist es nützlich, sich vor Augen zu halten, dass wir verschiedene Arten von „externen Beobachtern“ haben. In einem Fall ist der Beobachter ein Unit-Test, der auf eine Methode einer Java-Klasse schaut. In einem anderen Fall ist es ein „API-Client“, dem JSON und HTTP-Semantik wichtig sind. Im dritten Fall ist die Datenbank das System, das Constraints tatsächlich durchsetzen und korrekte Abfragen gewährleisten muss. Im vierten Fall ist es ein externer HTTP-Moderation-Service, mit dem wir über RestClient kommunizieren. Der Decision Tree beginnt genau hier: Zuerst benennen Sie das beobachtbare Verhalten, und dann wählen Sie, welches „Mikroskop“ nötig ist, um dieses Verhalten zu sehen.

3. Schritt 1: Wenn ein einfacher Unit-Test ausreicht

Diese Frage scheint beleidigend, weil Sie ja scheinbar einen Kurs über Spring Boot Testing besuchen, und ich schlage vor, mit „Vielleicht braucht man Spring gar nicht“ zu beginnen. Aber genau das ist erwachsene Ingenieursgewohnheit: Wenn ein Werkzeug teuer ist, darf man es nicht standardmäßig einschalten. Ein Unit-Test ist wie ein Fahrrad: Nicht das coolste Verkehrsmittel der Welt, aber auf kurzen Strecken und in der Stadt schlägt es fast alles.

Wenn die zu testende Logik in einer gewöhnlichen Klasse lebt und nicht von Spring-Magie, Container, Autokonfiguration, Serialisierung, Transaktionen und anderen „kostenpflichtigen Optionen“ abhängt, dann ist die richtige Antwort meistens ein Unit-Test. In ContentHub gibt es viele solcher Stellen: die Regeln für Statusübergänge in PublicationPolicy, die Slug-Generierung in SlugService, die lokale Anhangsprüfung in AttachmentValidationService. Sogar ArticleWorkflowService haben wir bereits auf Unit-Ebene getestet, indem wir die Zeit über Clock und den aktuellen Benutzer über einen Provider stabilisiert haben, anstatt die gesamte Anwendung zu laden.

Wichtig zu beachten: „Brauche ich Spring?“ ist nicht die Frage, ob Spring irgendwo im Projekt verwendet wird. Es ist die Frage, ob Spring für den Beweis einer bestimmten Aussage benötigt wird. Wenn Sie beweisen wollen, dass Spring Boot Testing zu spring-boot-testing wird, dann hilft Ihnen Spring nicht. Es wird nur danebensitzen und Ressourcen verbrauchen, wie eine Katze, die in die Küche kommt „nur um zu gucken“, aber plötzlich die Hälfte der Sahne frisst.

Ein kleines Beispiel, das daran erinnert, dass der Unit-Test auch in einem Spring-Boot-Projekt die Norm bleibt:

import org.junit.jupiter.api.Test; 
import static org.assertj.core.api.Assertions.assertThat;

class SlugServiceTest {

    @Test
    void convertsTitleToSlug() {
        // Arrange: Erstellen des zu testenden Dienstes ohne Spring-Kontext.
        SlugService service = new SlugService();

        // Act + Assert: Methode aufrufen und Ergebnis prüfen.
        assertThat(service.toSlug("Spring Boot Testing"))
                .isEqualTo("spring-boot-testing");
    }
}

Hier gibt es keine einzige Spring-Annotation – und das ist wunderbar. Der Test ist billig, schnell, vorhersagbar und schlägt genau dort fehl, wo die Logik beschädigt ist.

4. Schritt 2: Wenn Spring benötigt wird – die Prüfgrenze wählen

Wenn die Antwort „Ohne Spring geht es nicht“ bereits gefallen ist, muss man nicht wieder die ganze Liste der Annotationen ausbreiten. Hier ist es wichtiger, die verdächtige Grenze schnell mit dem zusammenzubringen, was der Test überhaupt beweisen soll.

Der JSON-Vertrag des DTO ist kaputt → @JsonTest. Dies fixiert Serialisierung und Deserialisierung, ohne MVC, Security und DB mitzuschleppen.

Die HTTP-Grenze des Controllers steht unter Verdacht → @WebMvcTest. Er prüft Mapping, Statuscodes, Header, Binding und Validierung auf MVC-Ebene.

Das Risiko lebt in der Persistenzgrenze → @DataJpaTest. Er prüft JPA-Mapping, Constraints, Queries, Sortierung und die Arbeit der Repositories.

Problem im externen HTTP-Client → @RestClientTest. Er fixiert das Verhalten des Adapters und den Kommunikationsvertrag nach außen.

Der Defekt zeigt sich nur an der Schnittstelle mehrerer Schichten → @SpringBootTest. Er prüft das Wiring und mehrere entscheidende durchgehende Verknüpfungen, nicht die lokale Mechanik einer einzelnen Schicht.

Diese kurze Aufstellung reicht aus, um im Decision Tree weiterzugehen. Hier ist nicht der zweite Atlas der Annotationen wichtig, sondern der schnelle Übergang vom Risiko zum minimal ausreichenden Kontext.

5. Schritt 3: Slice oder Full Context – es geht um den Beweis, nicht um Bequemlichkeit

Wenn man bereits weiß, dass Spring nötig ist, und sich an den Preis des Kontexts erinnert, kann man die Motive leicht verwechseln. Das gefährlichste Motiv klingt so: „Es ist einfacher für mich, den Test im vollen Kontext zu schreiben, weil da ja schon alles drin ist.“ Ja, es ist einfacher. Aber dieses „einfacher“ wird später meistens bezahlt: durch langsame Ausführung, instabile Diagnose und die Versuchung, einen großen Test statt mehrerer starker Tests zu schreiben.

Ein Slice-Test ist ein schmaler Kontext, der nur einen Teil der Anwendung hochfährt. Er macht den Test schneller und präziser im Sinne, weil man genau die Grenze testet. Full Context ist ein Test der „Systemintegration“ der Anwendung, der als Absicherung des Wirtings und für einige entscheidende durchgehende Szenarien nützlich ist, aber als universeller Hammer schädlich ist.

Innerhalb des Decision Trees kann man diese Frage ehrlich stellen: „Kann der Fehler innerhalb einer Schicht auftreten oder nur an der Schnittstelle der Schichten?“ Wenn innerhalb einer Schicht, erhöht der Full Context nicht die Beweiskraft, er fügt nur Rauschen hinzu. Wenn der Defekt jedoch nur beim Zusammenspiel der Schichten auftritt, kann der Slice-Test zu schmal sein und das Problem einfach nicht reproduzieren.

Und genau hier kommt der praktische Trick: Wenn Sie unsicher sind, versuchen Sie mit einem schmalen Test zu beginnen, der direkt der verdächtigen Schicht entspricht. Wenn er das Problem nicht abfängt oder Sie die Prüfung innerhalb der Schicht nicht korrekt ausdrücken können, ist das bereits ein Signal, dass der Defekt systemisch ist, und dann können Sie zum vollen Kontext erweitern. Der Full Context ist also der „zweite Versuch“, nicht die erste Reaktion.

6. Das Schema des Decision Tree und wie man es nutzt

Schemata werden geliebt, weil sie „Gefühle“ in Algorithmen verwandeln. Aber es ist wichtig zu bedenken: Der Decision Tree ist kein Naturgesetz. Er ist ein Werkzeug, um keine Dummheiten aus Trägheit zu machen. Nutzen Sie ihn wie einen Navigator: Er wird Ihnen nicht verbieten, vom Kurs abzuweichen, aber er wird zumindest fragen: „Sind Sie sicher?“

Unten ist ein einfaches Schema zur Auswahl der Testebene für ContentHub. Es beginnt mit dem Risiko, nicht mit der Annotation. Und das ist der entscheidende Punkt: Solange Sie das Risiko nicht formulieren können, wählen Sie keinen Test – Sie wählen eine „Stimmung“.

flowchart TD
    A["Gibt es einen Defekt/ein Risiko. Was möchten wir beweisen?"] --> B{"Kann es ohne Spring bewiesen werden?"}

    B -->|Ja| U["Plain Unit-Test (JUnit 6 + AssertJ + Mockito bei Bedarf)"]

    B -->|Nein| C{"Welche Grenze ist Gegenstand der Prüfung?"}

    C --> J["JSON-Vertrag des DTO"] --> JT["@JsonTest"]
    C --> W["HTTP-Grenze des Controllers (MVC)"] --> WT["@WebMvcTest"]
    C --> D["Persistence Boundary: Mapping/Constraints/Queries"] --> DT["@DataJpaTest"]
    C --> R["Outbound HTTP-Client (RestClient-Adapter)"] --> RT["@RestClientTest"]
    C --> F["Schnittstellen der Schichten / Wiring der gesamten Anwendung"] --> FT["@SpringBootTest"]

Wie nutzt man dieses Schema in der Praxis? Ganz einfach: Sie nehmen einen konkreten „Defekt“, formulieren ihn als Aussage, die Sie beweisen wollen, und gehen den Baum entlang. Wenn Sie an der ersten Weggabelung nicht ehrlich antworten können, ob Spring nötig ist, bedeutet das normalerweise, dass Sie noch nicht verstanden haben, was Sie genau testen. Es ist besser, 2 Minuten für die Präzisierung der Formulierung aufzuwenden als 20 Minuten für das Starten eines unnötigen Kontexts.

7. Den Decision Tree auf ContentHub anwenden: lebendige Situationen

Theorie ist genau so lange nützlich, bis sie auf einen echten Bug trifft. Deshalb nehmen wir typische Situationen aus ContentHub und schauen, wie der Decision Tree uns zum minimal ausreichenden Test führt. Hierbei ist nicht nur wichtig, „welche Annotation“, sondern auch, „warum gerade diese Annotation den notwendigen Beweis liefert“.

Die Regeln für Statusübergänge des Artikels sind kaputt

Stellen wir uns vor, jemand hat versehentlich den Übergang PUBLISHED → IN_REVIEW erlaubt. In der UI könnte das so aussehen: „Der Artikel ist bereits veröffentlicht, kann aber trotzdem erneut zur Überprüfung eingereicht werden.“ Weder Controller noch Datenbank werden hier benötigt, denn es handelt sich um eine reine Geschäftsregel. Also sagt der Decision Tree: Spring wird nicht benötigt, nehmen wir einen Unit-Test.

Das Mini-Gerüst des Tests wird maximal langweilig sein – und das ist ein Kompliment:

import org.junit.jupiter.api.Test; 
import static org.assertj.core.api.Assertions.assertThat;

class PublicationPolicyTest {

    @Test
    void doesNotAllowPublishedToReview() {
        // Arrange: Testen der Geschäftsregel als normalen Java-Code.
        PublicationPolicy policy = new PublicationPolicy();

        // Assert: Verbot des spezifischen Statusübergangs fixieren.
        assertThat(policy.canMove(ArticleStatus.PUBLISHED, ArticleStatus.IN_REVIEW))
                .isFalse();
    }
}

Ein solcher Test fängt den Defekt schnell und billig. Und angenehm ist, dass er nicht kaputtgeht, wenn Sie die Spring-Konfiguration oder die Controller ändern, denn das ist nicht seine Aufgabe.

Der Client versteht JSON nicht mehr: Feldname im DTO geändert

Angenommen, in ArticleDetailsResponse wurde das Feld authorUsername in author umbenannt, weil „das schöner aussieht“. Aus Sicht des Java-Codes kann alles perfekt sein: Das Objekt wird gebaut, der Controller gibt zurück, Status 200, der Dienst funktioniert. Aber der externe Client erhält plötzlich ein anderes JSON und stürzt ab. Das ist kein Problem des Web-Mappings. Es ist genau ein Problem des JSON-Vertrags. Also @JsonTest.

Wichtig: Wir müssen den Controller nicht hochfahren, um die Serialisierung des DTO zu prüfen. Der JSON-Slice reicht aus.

import org.junit.jupiter.api.Test; 
import org.springframework.boot.test.autoconfigure.json.JsonTest;

@JsonTest
class ArticleDetailsResponseJsonTest {

    @Test
    void serializesAuthorUsernameField() {
        // Hier wird die Anwesenheit/der Name des Feldes im JSON geprüft (Implementierung jetzt nicht geschrieben).
        // Idee: Der Test schlägt fehl, wenn der DTO-Vertrag "versehentlich" geändert wird.
    }
}

Ja, der Test ist leer, aber die Wahl ist richtig: Wir haben den Test auf den Vertrag ausgerichtet. Und wenn der Vertrag versehentlich geändert wird, leuchtet diese Stelle zuerst auf.

Der Endpoint gibt den falschen Status zurück oder akzeptiert keinen Parameter

Nun eine andere Art von Problem. Angenommen, GET /api/public/articles/{slug} beginnt plötzlich 404 zurückzugeben, selbst für einen existierenden veröffentlichten Artikel. Oder der Controller akzeptiert keine sort/page-Parameter mehr. Das ist eine Web-Grenze: Mapping, Path Variables, Query-Parameter, Status-Semantik. Hier kann der Unit-Test des Dienstes grün sein, weil der Dienst gar nicht weiß, wie aus der URL die Controller-Methode wird. Ein Data-Test hilft auch nicht, weil die Datenbank perfekt sein kann.

Also @WebMvcTest.

import org.junit.jupiter.api.Test; 
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;

@WebMvcTest(PublicArticleController.class)
class PublicArticleControllerWebMvcTest {

    @Test
    void returnsOkForExistingSlug() {
        // Hier wird das HTTP-Verhalten des Controllers geprüft (Status, JSON, Parameter).
        // Wichtig: Prüfgegenstand ist der Controller als "Übersetzer", nicht die Geschäftslogik des Dienstes.
    }
}

Die Bedeutung dieser Wahl ist: Wir testen den „Übersetzer“ zwischen HTTP und Dienst. Und genau dort leben solche Defekte.

Das Repository filtert veröffentlichte Artikel nicht mehr

Angenommen, die öffentliche Liste zeigt plötzlich DRAFT-Artikel an. Das ist fast sicher ein Persistenz/Query-Problem: entweder eine falsche Abfrage, oder eine falsche Filterung im Repository, oder ein falsches Status-Mapping. Das über @WebMvcTest zu testen, ist sinnlos: Im Web-Slice gibt es keine Repositories. Das über @SpringBootTest zu testen, ist möglich, aber teuer und kann die Ursache verschleiern.

Also @DataJpaTest.

import org.junit.jupiter.api.Test; 
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

@DataJpaTest
class PublishedArticleQueryDataJpaTest {

    @Test
    void findsOnlyPublishedArticles() {
        // Hier wird das Query-Verhalten geprüft (welche Artikel in die Auswahl fallen).
        // Idee: Daten in der DB vorbereiten, Repository-Query ausführen, Zusammensetzung der Ergebnisse prüfen.
    }
}

Dies ist der Fall, in dem der Data-Slice die maximale Beweiskraft für ein spezifisches Risiko liefert: Wir prüfen nicht HTTP, wir prüfen nicht die Serialisierung, wir prüfen, ob die „DB-Grenze die Wahrheit sagt“.

Der Moderation-Client verarbeitet die Antwort des externen Dienstes falsch

Eine weitere typische Geschichte: Der Moderation-Service beginnt, ein neues Antwortformat oder einen anderen Status zurückzugeben, und unser RestClientModerationClient kann das nicht lesen. Infolgedessen schlägt die Einreichung zur Überprüfung fehl, obwohl die Geschäftslogik und die Repositories in Ordnung sind. Das ist eine Outbound-Integrationsgrenze: der Client-Adapter.

Die richtige Wahl ist @RestClientTest.

import org.junit.jupiter.api.Test; 
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;

@RestClientTest(RestClientModerationClient.class)
class RestClientModerationClientTest {

    @Test
    void mapsBlockDecisionFromExternalService() {
        // Hier wird die Anfrage/Antwort/das Mapping des Moderation-Payloads geprüft.
        // Wichtig: Wir fixieren den Kommunikationsvertrag mit dem externen HTTP-Dienst.
    }
}

Warum ist das minimal ausreichend? Weil wir nicht den gesamten Workflow prüfen, sondern das spezifische Risiko: „Der Adapter kommuniziert korrekt per HTTP“. Und das kann bewiesen werden, ohne die gesamte Anwendung hochzufahren.

Bug an der Schnittstelle der Schichten → @SpringBootTest

Und schließlich die unangenehmste Klasse von Defekten: Einzeln betrachtet sieht alles richtig aus. Unit-Tests der Dienste sind grün, der JSON-Vertrag des DTO scheint korrekt, das Repository funktioniert auf seine Weise. Aber sobald man die Anwendung startet, schlägt die echte Anfrage fehl: sei es wegen des Wirtings, der Konfiguration oder weil eine Schicht etwas erwartet und eine andere etwas anderes liefert.

Hier entsteht die Rechtfertigung für @SpringBootTest. Denn Sie müssen genau die Verkettung beweisen: Controller → Service → Repository, plus Einstellungen und Infrastruktur. Ein solcher Test ist teuer, aber er fängt Defekte „zwischen den Schichten“, die lokal nicht abgefangen werden können.

import org.junit.jupiter.api.Test; 
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class ArticlePublicationFlowIntegrationTest {

    @Test
    void fullContextWiringWorks() {
        // Hier wird eine durchgehende Prüfung durchgeführt: Der Kontext wird zusammengebaut, die Schichten sind verbunden.
        // Normalerweise ist dies der Ort für einige wenige entscheidende Happy-Path/Critical-Path-Szenarien.
    }
}

Und dies ist der entscheidende Punkt des Decision Tree: Der Full Context ist die Antwort auf einen bestimmten Risikotyp, nicht eine „universelle Gewohnheit“.

8. Typische Fehler bei der Auswahl des Boot-Testtyps

Fehler Nr. 1: Mit der Annotation beginnen, nicht mit dem Risiko.
Die häufigste Falle klingt so: „Ich brauche einen Test … wahrscheinlich @SpringBootTest.“ Das ähnelt der Situation „Ich möchte etwas essen … wahrscheinlich bestelle ich die gesamte Speisekarte“. Das Ergebnis ist ein teurer Test, der unklar beweist und noch unklarer ist, warum er fehlgeschlagen ist. Die Lösung ist einfach: Formulieren Sie zuerst die Aussage, die Sie beweisen wollen, und wählen Sie erst dann den minimalen Kontext.

Fehler Nr. 2: Spring hochfahren, wo ein Unit-Test ausreicht.
Das tut besonders weh, weil es „professionell“ aussieht: ein Test mit Spring-Annotation, Kontext, Autokonfiguration – wunderschön. Aber wenn Sie SlugService oder PublicationPolicy testen, fügt Spring nur Kosten hinzu, keinen Wert. Ein solcher Test wird langsamer, schwieriger zu debuggen und letztendlich häufiger „nicht gestartet“. Und ein Test, den man nicht starten möchte, wird im entscheidenden Moment normalerweise nicht gestartet.

Fehler Nr. 3: Einen zu schmalen Slice für einen systemischen Defekt wählen.
Es gibt auch das gegenteilige Extrem: Sie vermuten, dass das Problem an der Schnittstelle der Schichten liegt, aber versuchen, es mit einem Slice-Test zu beweisen, bei dem die Hälfte der benötigten Komponenten einfach nicht vorhanden ist. Infolgedessen „reproduziert der Test den Bug nicht“, und Sie ziehen die falsche Schlussfolgerung, dass alles in Ordnung sei. Die richtige Disziplin hier ist: Wenn der Defekt nur in der Verkettung auftritt, testen Sie die Verkettung. Ein Slice ist gut, wenn das Risiko tatsächlich innerhalb einer Schicht lebt.

Fehler Nr. 4: Dieselbe Prüfung auf drei Ebenen ohne neue Beweiskraft duplizieren.
Manchmal möchte man „zur Sicherheit“ dasselbe mit einem Unit-Test, mit @WebMvcTest und mit @SpringBootTest prüfen. In der Praxis wird daraus oft der dreifache Preis und die dreifache Zerbrechlichkeit, aber nicht die dreifache Sicherheit. Es ist viel nützlicher, die Prüfungen über die Schichten so zu verteilen, dass jeder Test etwas Eigenes beweist: Unit – die Geschäftsregel, Slice – den Vertrag der Grenze, Full Context – das Wiring.

Fehler Nr. 5: Mehrere Slice-Annotationen zu einem Super-Test kombinieren wollen.
Die Intuition sagt: „Wenn ich @WebMvcTest und @DataJpaTest zusammen setze, bekomme ich einen Web+Data-Test.“ Aber so funktioniert es nicht: Slices sind bewusst als sich gegenseitig ausschließende schmale Kontexte konzipiert, nicht als Lego-Baukasten. Wenn Sie den vollen Satz von Schichten brauchen, ist das bereits eine Full-Context-Geschichte. Wenn Sie nur eine Schicht brauchen, wählen Sie einen einzigen Slice.

1
Aufgabe
Spring Test, Level 6, Lektion 4
Gesperrt
Enum-Karte zur Auswahl des Testtyps
Enum-Karte zur Auswahl des Testtyps
1
Aufgabe
Spring Test, Level 6, Lektion 4
Gesperrt
Decision tree mit Fragen zu Spring und Cross-Layer
Decision tree mit Fragen zu Spring und Cross-Layer
1
Umfrage/Quiz
Spring-Tests, Level 6, Lektion 4
Nicht verfügbar
Spring-Tests
Kontext und Annotationen
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION