Übungsaufgaben zu Service-Schicht und Dependency Injection: Mitfahrgesuche einstellen und zurückziehen

https://bildung.social/@oerinformatik/

https://oer-informatik.de/spring-boot-service-dependency-injection-uebungsaufgaben

Service-Schicht, Dependency Injection, Konstruktorinjektion, Transactional, Transaktion, Mockito, Mock, Komponententest, JaCoCo, Abdeckungs-Gate, Übungsaufgaben

tl/dr; (ca. 33 min Lese- und Bearbeitungszeit): Übungen zur Service-Schicht im Mitfahr-Backend: Du erkennst die drei Arten der Dependency Injection, beurteilst Aussagen zu Mocks und Transaktionen, schreibst einen GesuchService mit Konstruktorinjektion und einer injizierten Uhr, testest ihn mit Mockito und findest heraus, warum ein zurückgezogenes Gesuch trotz grüner Tests offen bleibt und warum das Abdeckungs-Gate ein anderes Paket meldet als erwartet [AI-Assisted]. (Zuletzt geändert am 11.11.2026)

Dieser Artikel ist Teil einer Artikelreihe über ein REST-Backend mit Spring Boot: Überblick und Fahrplan / 01 Anforderungen und API-Vertrag / 02 Projekt anlegen / 03 Codegenerierung / 04 Fachlogik und Komponententest / 05 Build-Qualität und CI / 06 Persistenz mit JPA / 07 Repository-Integrationstest / 08 Service und Dependency Injection / Übungsaufgaben zu 08

Diese Übungsaufgaben gehören zum Artikel Fachlogik verdrahten: Service-Schicht, Dependency Injection und Tests mit Mockito und setzen den Übungsstrang zu den Mitfahrgesuchen fort. Ausgangspunkt ist das Projekt im Ordner src/08-service-dependency-injection des Repositorys, ergänzt um die Klassen aus den bisherigen Übungen: Zeitfensterregel mit ZeitfensterUngueltigException und Gesuchstatus (Übung 04), die Entity Gesuch und das GesuchRepository (Übung 06). Die Entity hat Getter für alle Attribute und eine neue Methode, mit der ein Gesuch zurückgezogen wird:

    public void zieheZurueck() {
        if (!status.erlaubtWechselZu(Gesuchstatus.ZURUECKGEZOGEN)) {
            throw new GesuchNichtOffenException(status);
        }
        status = Gesuchstatus.ZURUECKGEZOGEN;
    }

GesuchNichtOffenException erbt von ZustandskonfliktException. Außerdem gibt es eine fachliche Ausnahme und einen Record für die Eingaben eines neuen Gesuchs (im Paket service):

public class GesuchUnzulaessigException extends FachregelVerletztException {

    public GesuchUnzulaessigException(String grund) {
        super(grund);
    }
}
public record NeuesGesuch(Long nutzerId, Long haltepunktId, Fahrtrichtung richtung, LocalDate datum,
        LocalTime zeitVon, LocalTime zeitBis) {
}
Fahrplan der Artikelreihe im V-Modell; auf der Ebene Komponententest ist Artikel 08 hervorgehoben (mit KI erstellt)

Hinweise zur Bearbeitung: Bis zur Teil-1-Prüfung der IT-Berufe solltest Du die Basiskompetenz beherrschen, also Aufgabe 1.1 und Aufgabe 1.2. Als FIA, FIDP oder FIDV musst Du zur Teil-2-Prüfung Aufgaben auf Kernkompetenz-Ebene lösen können, das sind Aufgabe 2.1 und Aufgabe 2.2. Von FIA wird zusätzlich erwartet, dass ihnen Aufgaben auf Profilkompetenz-Ebene (Aufgabe 3.1 und Aufgabe 3.2) keine Probleme bereiten. Notiere Deine Antworten zuerst vollständig, bevor Du die Beispiellösung aufklappst. Code und Tests kannst Du anschließend im Projekt aus dem Repository mit dem Build ausführen; andere fachlich richtige Lösungen sind ebenso richtig.

Du möchtest eigene Aufgaben zum Üben von einem KI-Sprachmodell generieren lassen?

Kopiere den Text in ein KI-Sprachmodell Deiner Wahl und passe Kompetenzstufe, Anzahl, Bearbeitungszeit und Szenario bei Bedarf an. Wie die Aufgaben aufgebaut sind, beschreibt der Artikel Wie sind die Aufgaben aufgebaut?. Prüfe die Ergebnisse mit dem Lehrartikel: KI-Sprachmodelle machen Fehler.

Erstelle neue Übungsaufgaben zum Thema „Service-Schicht und Dependency
Injection: Mitfahrgesuche einstellen und zurückziehen“.

Bauplan: Halte Dich an Aufbau, Kompetenzstufen, Operatoren,
Anforderungsbereiche, Punkte und Zeitschätzung, die im Artikel
https://oer-informatik.de/aufgaben-aufbau beschrieben sind.

Fachliche Grundlage:
- https://oer-informatik.de/spring-boot-service-dependency-injection
- https://oer-informatik.de/dependency-injection
- https://oer-informatik.de/unit-tests
- https://oer-informatik.de/codecoverage
- Stilbeispiel: https://oer-informatik.de/spring-boot-service-dependency-injection-uebungsaufgaben

Die Aufgaben sollen Inhalte zu diesen Schlagwörtern abdecken:
- Service-Schicht
- Dependency Injection
- Konstruktorinjektion
- Transactional
- Transaktion
- Mockito
- Mock
- Komponententest
- JaCoCo
- Abdeckungs-Gate

Kompetenzstufe: Basiskompetenz (alternativ: Kernkompetenz, Profilkompetenz, Expertise)
Anzahl: 1 Aufgabe mit Teilaufgaben
Bearbeitungszeit in Minuten ca.: <5 bis 30 min>
Szenario: eines der Szenarien aus dem Bauplan

Vorgehen:
1. Lies zuerst die verlinkten Artikel. Kannst Du keine Webseiten
   abrufen, sag mir das, statt zu raten.
2. Steht bei einer Angabe noch ein Platzhalter in <…>, frag mich
   zuerst danach. Richte Punkte und Zahl der Teilaufgaben nach der
   Bearbeitungszeit (ca. 0,9 min je Punkt).
3. Gib je Aufgabe Titel mit Kompetenzstufe, Ausgangssituation und
   Teilaufgaben mit Operator und Punktzahl aus, dazu die geschätzte
   Bearbeitungszeit.
4. Gib die Beispiellösungen mit Punkteverteilung erst aus, wenn ich
   danach frage.

Aufgabe 1.1: Drei Wege der Injektion erkennen (Basiskompetenz)

Drei Entwürfe für einen GesuchController bekommen den GesuchService auf unterschiedliche Weise:

// Entwurf A
@RestController
public class GesuchController implements GesucheApi {

    @Autowired
    private GesuchService gesuchService;
}

// Entwurf B
@RestController
public class GesuchController implements GesucheApi {

    private final GesuchService gesuchService;

    public GesuchController(GesuchService gesuchService) {
        this.gesuchService = gesuchService;
    }
}

// Entwurf C
@RestController
public class GesuchController implements GesucheApi {

    private GesuchService gesuchService;

    @Autowired
    public void setGesuchService(GesuchService gesuchService) {
        this.gesuchService = gesuchService;
    }
}
  1. Benenne für jeden Entwurf die Art der Dependency Injection. (3 Punkte)

  2. Begründe, welchen Entwurf Du für einen Test ohne Spring am einfachsten findest. (1 Punkt)

  1. Je Entwurf 1 Punkt: A ist Feldinjektion (@Autowired am Attribut), B ist Konstruktorinjektion (einziger Konstruktor, Spring braucht kein @Autowired), C ist Setterinjektion (@Autowired an der Setter-Methode).

  2. Entwurf B: Im Test reicht new GesuchController(gesuchServiceMock); das Attribut ist final und nach dem Konstruktor sicher gesetzt. Bei A geht das nur per Reflection, bei C kann man den Setter vergessen und hat dann ein unvollständiges Objekt (1 Punkt).

Aufgabe 1.2: Aussagen zu Mocks und Transaktionen beurteilen (Basiskompetenz)

Beurteile jede Aussage als richtig oder falsch und begründe Deine Entscheidung in einem Satz. (4 Punkte)

  1. Ein Mock des GesuchRepository führt die Abfragen des echten Repositorys aus, nur ohne Datenbank.
  2. In einer Methode mit @Transactional werden Änderungen an geladenen Entities beim Commit gespeichert, auch wenn niemand save aufruft.
  3. Ruft eine Methode des GesuchService eine andere Methode desselben Service auf, die mit @Transactional markiert ist, läuft diese immer in einer eigenen Transaktion.
  4. Wenn das Abdeckungs-Gate 100 % Zweigabdeckung im Paket service meldet, sind alle Fachregeln richtig umgesetzt.

Je Aussage mit Begründung 1 Punkt:

  1. Falsch. Ein Mock hat keine eigene Logik; er gibt leere Werte oder das zurück, was der Test mit when(…).thenReturn(…) festgelegt hat.
  2. Richtig. Hibernate erkennt beim Commit geänderte Entities (Dirty Checking) und schreibt sie; nur in einer Transaktion mit readOnly = true unterbleibt das.
  3. Falsch. Der Aufruf innerhalb derselben Klasse geht am Proxy vorbei (self-invocation); die Annotation der aufgerufenen Methode wirkt dann nicht.
  4. Falsch. Die Abdeckung zeigt nur, dass jeder Zweig ausgeführt wurde, nicht, dass ein Test das Ergebnis geprüft hat; auch ein falscher Vergleich wie < statt <= kann vollständig abgedeckt sein.

Aufgabe 2.1: Einen GesuchService schreiben (Kernkompetenz)

Ein Gesuch soll über einen Service eingestellt werden. Dabei gelten diese Regeln:

  • Nutzerin bzw. Nutzer und Haltepunkt müssen existieren, sonst NichtGefundenException.
  • Ein Gesuch für einen Tag vor dem heutigen ist nicht erlaubt (GesuchUnzulaessigException); ein Gesuch für heute ist erlaubt. Was „heute“ ist, bestimmt die Clock-Bean aus dem Lehrartikel.
  • Das Zeitfenster muss die Zeitfensterregel erfüllen.
  • Nur ein Gesuch, das alle Regeln erfüllt, wird gespeichert.

Schreibe die Klasse GesuchService mit der Methode Gesuch stelleEin(NeuesGesuch neu). Spring soll alle Abhängigkeiten per Konstruktor einsetzen; lesende Methoden sollen in einer Transaktion ohne Schreibrecht laufen. Importe musst Du nicht notieren. (8 Punkte)

@Service
@Transactional(readOnly = true)
public class GesuchService {

    private final GesuchRepository gesuchRepository;
    private final NutzerRepository nutzerRepository;
    private final HaltepunktRepository haltepunktRepository;
    private final Clock uhr;

    public GesuchService(GesuchRepository gesuchRepository, NutzerRepository nutzerRepository,
            HaltepunktRepository haltepunktRepository, Clock uhr) {
        this.gesuchRepository = gesuchRepository;
        this.nutzerRepository = nutzerRepository;
        this.haltepunktRepository = haltepunktRepository;
        this.uhr = uhr;
    }

    @Transactional
    public Gesuch stelleEin(NeuesGesuch neu) {
        Nutzer nutzer = nutzerRepository.findById(neu.nutzerId())
                .orElseThrow(() -> new NichtGefundenException("Nutzer", neu.nutzerId()));
        Haltepunkt haltepunkt = haltepunktRepository.findById(neu.haltepunktId())
                .orElseThrow(() -> new NichtGefundenException("Haltepunkt", neu.haltepunktId()));
        if (neu.datum().isBefore(LocalDate.now(uhr))) {
            throw new GesuchUnzulaessigException("Ein Gesuch kann nicht für einen vergangenen Tag gestellt werden.");
        }
        Zeitfensterregel.pruefe(neu.zeitVon(), neu.zeitBis());
        return gesuchRepository.save(new Gesuch(nutzer, haltepunkt, neu.richtung(), neu.datum(),
                neu.zeitVon(), neu.zeitBis()));
    }

    // zieheZurueck siehe Aufgabe 3.1
}

Punkte: @Service (1), @Transactional(readOnly = true) an der Klasse und @Transactional an stelleEin (1), vier final-Attribute mit einem Konstruktor (2), Nutzer und Haltepunkt mit orElseThrow und NichtGefundenException (1), Datum gegen LocalDate.now(uhr) mit isBefore, sodass heute erlaubt ist (2), Zeitfensterregel vor dem Speichern und save mit neuem Gesuch (1). Eine andere Reihenfolge der Prüfungen ist ebenso richtig, solange save zuletzt kommt.

Aufgabe 2.2: Den GesuchService mit Mockito testen (Kernkompetenz)

Oksana Melnyk (Nutzer 3) möchte am Montag, dem 23.11.2026, zwischen 06:45 und 07:15 Uhr vom Hauptbahnhof Gütersloh (Haltepunkt 1, 18,6 km) zur Schule mitfahren. Die Tests sollen so laufen, als wäre es Freitag, der 20.11.2026, 12:00 Uhr.

Schreibe die Testklasse GesuchServiceTest mit Mockito, ohne Spring zu starten. Prüfe, dass Oksanas Gesuch mit den richtigen Werten gespeichert wird, dass ein Gesuch für den 20.11. erlaubt und eines für den 19.11. abgelehnt wird und dass ein Zeitfenster von 07:15 bis 06:45 Uhr nicht gespeichert wird. Importe musst Du nicht notieren. (8 Punkte)

@ExtendWith(MockitoExtension.class)
class GesuchServiceTest {

    /** Freitag, 20.11.2026, 12:00 Uhr in Deutschland. */
    private static final Clock UHR = Clock.fixed(Instant.parse("2026-11-20T11:00:00Z"), ZoneId.of("Europe/Berlin"));

    @Mock
    private GesuchRepository gesuchRepository;

    @Mock
    private NutzerRepository nutzerRepository;

    @Mock
    private HaltepunktRepository haltepunktRepository;

    private GesuchService gesuchService;

    private final Nutzer oksana = new Nutzer("Kaffeetasse", "Oksana", "Melnyk", LocalDate.of(2003, 4, 28), null,
            false);
    private final Haltepunkt guetersloh = new Haltepunkt("Hauptbahnhof, ZOB Steig 3", "Gütersloh",
            new BigDecimal("18.6"));

    @BeforeEach
    void setUp() {
        gesuchService = new GesuchService(gesuchRepository, nutzerRepository, haltepunktRepository, UHR);
    }

    private void nutzerUndHaltepunktVorhanden() {
        when(nutzerRepository.findById(3L)).thenReturn(Optional.of(oksana));
        when(haltepunktRepository.findById(1L)).thenReturn(Optional.of(guetersloh));
    }

    private static NeuesGesuch gesuchAm(LocalDate datum, LocalTime von, LocalTime bis) {
        return new NeuesGesuch(3L, 1L, Fahrtrichtung.ZUR_SCHULE, datum, von, bis);
    }

    @Test
    void stelleEinSpeichertGueltigesGesuch() {
        nutzerUndHaltepunktVorhanden();
        when(gesuchRepository.save(any(Gesuch.class))).thenAnswer(aufruf -> aufruf.getArgument(0));

        gesuchService.stelleEin(gesuchAm(LocalDate.of(2026, 11, 23), LocalTime.of(6, 45), LocalTime.of(7, 15)));

        ArgumentCaptor<Gesuch> gespeichert = ArgumentCaptor.forClass(Gesuch.class);
        verify(gesuchRepository).save(gespeichert.capture());
        assertThat(gespeichert.getValue().getNutzer()).isSameAs(oksana);
        assertThat(gespeichert.getValue().getZeitVon()).isEqualTo(LocalTime.of(6, 45));
        assertThat(gespeichert.getValue().getStatus()).isEqualTo(Gesuchstatus.OFFEN);
    }

    @Test
    void gesuchFuerHeuteIstErlaubt() {
        nutzerUndHaltepunktVorhanden();
        when(gesuchRepository.save(any(Gesuch.class))).thenAnswer(aufruf -> aufruf.getArgument(0));

        Gesuch gesuch = gesuchService.stelleEin(gesuchAm(LocalDate.of(2026, 11, 20), LocalTime.of(15, 0),
                LocalTime.of(16, 0)));

        assertThat(gesuch.getDatum()).isEqualTo(LocalDate.of(2026, 11, 20));
    }

    @Test
    void gesuchFuerGesternWirdAbgelehnt() {
        nutzerUndHaltepunktVorhanden();

        assertThatThrownBy(() -> gesuchService.stelleEin(gesuchAm(LocalDate.of(2026, 11, 19), LocalTime.of(6, 45),
                LocalTime.of(7, 15)))).isInstanceOf(GesuchUnzulaessigException.class);
        verify(gesuchRepository, never()).save(any());
    }

    @Test
    void ungueltigesZeitfensterWirdNichtGespeichert() {
        nutzerUndHaltepunktVorhanden();

        assertThatThrownBy(() -> gesuchService.stelleEin(gesuchAm(LocalDate.of(2026, 11, 23), LocalTime.of(7, 15),
                LocalTime.of(6, 45)))).isInstanceOf(ZeitfensterUngueltigException.class);
        verify(gesuchRepository, never()).save(any());
    }
}

Punkte: feste Uhr mit Clock.fixed in der deutschen Zeitzone (1), Mocks der drei Repositories und Service per Konstruktor (1), Speichern mit ArgumentCaptor oder Rückgabewert und Prüfung von Nutzerin, Zeit und Status (2), Grenzwerte 20.11. erlaubt und 19.11. abgelehnt (2), Zeitfenster abgelehnt (1), verify(…, never()).save(any()) bei den Ablehnungen (1). Die Tests laufen mit gradlew test bzw. mvnw test grün. Stubs, die ein Test nicht braucht, würde Mockito mit UnnecessaryStubbingException melden; deshalb legt jeder Test nur fest, was der Service bis zur geprüften Stelle abfragt.

Aufgabe 3.1: Grüne Tests, aber das Gesuch bleibt offen (Profilkompetenz)

Im GesuchService (mit @Transactional(readOnly = true) an der Klasse) steht diese Methode:

    public Gesuch zieheZurueck(Long gesuchId) {
        Gesuch gesuch = gesuchRepository.findById(gesuchId)
                .orElseThrow(() -> new NichtGefundenException("Gesuch", gesuchId));
        gesuch.zieheZurueck();
        return gesuch;
    }

Ein Mockito-Test, der ein Gesuch per Stub liefert, zieheZurueck(1L) aufruft und danach assertThat(gesuch.getStatus()).isEqualTo(Gesuchstatus.ZURUECKGEZOGEN) prüft, ist grün. Im Betrieb meldet Oksana aber, dass ihr zurückgezogenes Gesuch weiterhin offen ist. Ein Integrationstest bestätigt das:

@SpringBootTest
class GesuchServiceIntegrationTest {

    @Autowired
    private GesuchService gesuchService;

    @Autowired
    private GesuchRepository gesuchRepository;

    @Test
    void zurueckgezogenesGesuchIstGespeichert() {
        var gesuch = gesuchService.stelleEin(new NeuesGesuch(3L, 1L, Fahrtrichtung.ZUR_SCHULE,
                LocalDate.of(2027, 1, 11), LocalTime.of(6, 45), LocalTime.of(7, 15)));

        var rueckgabe = gesuchService.zieheZurueck(gesuch.getId());

        assertThat(rueckgabe.getStatus()).isEqualTo(Gesuchstatus.ZURUECKGEZOGEN);
        assertThat(gesuchRepository.findById(gesuch.getId()).orElseThrow().getStatus())
                .isEqualTo(Gesuchstatus.ZURUECKGEZOGEN);
    }
}
GesuchServiceIntegrationTest > zurueckgezogenesGesuchIstGespeichert() FAILED
    org.opentest4j.AssertionFailedError: 
    expected: ZURUECKGEZOGEN
     but was: OFFEN
  1. Erkläre, warum der Status in der Datenbank OFFEN bleibt, obwohl die Methode ihn ändert und keine Ausnahme auftritt. (2 Punkte)

  2. Erkläre, warum der Mockito-Test den Fehler nicht finden kann. (2 Punkte)

  3. Korrigiere die Methode und begründe, welche Testebene solche Fehler aufdeckt. (2 Punkte)

  1. Die Methode hat keine eigene Annotation, also gilt @Transactional(readOnly = true) der Klasse (1 Punkt). In einer Transaktion nur zum Lesen schreibt Hibernate geänderte Entities beim Ende nicht in die Datenbank; das Objekt im Speicher ist geändert, die Zeile in der Tabelle nicht, und es gibt keine Fehlermeldung (1 Punkt).

  2. Der Test ersetzt das Repository durch einen Mock und prüft nur das Java-Objekt im Speicher (1 Punkt). Transaktionen, Proxy und Hibernate sind gar nicht beteiligt, weil Spring nicht läuft; ob gespeichert wird, liegt außerhalb dessen, was ein Komponententest sehen kann (1 Punkt).

  3. Korrektur (1 Punkt):

    @Transactional
    public Gesuch zieheZurueck(Long gesuchId) {
        Gesuch gesuch = gesuchRepository.findById(gesuchId)
                .orElseThrow(() -> new NichtGefundenException("Gesuch", gesuchId));
        gesuch.zieheZurueck();
        return gesuch;
    }

Den Fehler deckt nur ein Test auf, der Spring mit Transaktionen und Datenbank startet und das Ergebnis neu aus der Datenbank liest, also ein Integrationstest wie der gezeigte mit @SpringBootTest (oder später ein Systemtest über HTTP). Nach der Korrektur ist er grün (1 Punkt).

Aufgabe 3.2: Das Gate meldet das falsche Paket? (Profilkompetenz)

Ein Teammitglied hat den GesuchService mit stelleEin und zieheZurueck (korrigiert wie in Aufgabe 3.1) eingecheckt. Der GesuchServiceTest enthält nur zwei Tests: Oksanas Gesuch wird gespeichert, ein Gesuch für gestern wird abgelehnt. Die Tests der Zeitfensterregel und des Gesuchstatus aus Übung 04 sind vorhanden. Der Job test der Pipeline scheitert:

> Task :jacocoTestCoverageVerification FAILED
[ant:jacocoReport] Rule violated for package de.dbh.mitfahrlabor.domain: branches covered ratio is 0.89, but expected minimum is 0.90
  1. Erkläre, warum das Gate das Paket domain meldet und nicht das Paket service, obwohl zieheZurueck im Service von keinem Test aufgerufen wird. (2 Punkte)

  2. Das Teammitglied schlägt vor, die Grenze für Zweige auf 85 % zu senken, weil „nur ein Prozent fehlt“. Bewerte den Vorschlag. (2 Punkte)

  3. Ergänze die fehlenden Tests im GesuchServiceTest. (2 Punkte)

  1. Für das Paket service prüft das Gate nur die Zweigabdeckung. GesuchService.zieheZurueck hat keine Verzweigung (das orElseThrow zählt JaCoCo nicht als Zweig), also fehlt dort kein Zweig, obwohl die Methode nie läuft (1 Punkt). Die Verzweigung steckt in Gesuch.zieheZurueck (if (!status.erlaubtWechselZu(…))); ihre zwei Zweige sind unbedeckt, deshalb sinkt das Paket domain auf 42 von 47 Zweigen, also 0,89 (1 Punkt). Eine zusätzliche Regel auf Anweisungen oder Methoden im Paket service hätte die Lücke direkt dort gemeldet.

  2. Abzulehnen (1 Punkt): Die Grenze soll verhindern, dass neuer Code ohne Tests hinzukommt; genau das ist passiert. Wer sie senkt, sobald sie anschlägt, macht sie wirkungslos. Grenzen werden nur angehoben, wenn die Abdeckung gestiegen ist (1 Punkt).

  3. Je Test 1 Punkt:

    @Test
    void zieheZurueckSetztStatus() {
        var gesuch = new Gesuch(oksana, guetersloh, Fahrtrichtung.ZUR_SCHULE, LocalDate.of(2026, 11, 23),
                LocalTime.of(6, 45), LocalTime.of(7, 15));
        when(gesuchRepository.findById(1L)).thenReturn(Optional.of(gesuch));

        gesuchService.zieheZurueck(1L);

        assertThat(gesuch.getStatus()).isEqualTo(Gesuchstatus.ZURUECKGEZOGEN);
    }

    @Test
    void zurueckgezogenesGesuchKannNichtNochmalZurueckgezogenWerden() {
        var gesuch = new Gesuch(oksana, guetersloh, Fahrtrichtung.ZUR_SCHULE, LocalDate.of(2026, 11, 23),
                LocalTime.of(6, 45), LocalTime.of(7, 15));
        gesuch.zieheZurueck();
        when(gesuchRepository.findById(1L)).thenReturn(Optional.of(gesuch));

        assertThatThrownBy(() -> gesuchService.zieheZurueck(1L)).isInstanceOf(GesuchNichtOffenException.class);
    }

Danach meldet check keine Verstöße mehr. Ein dritter Test für eine unbekannte ID (NichtGefundenException) wäre sinnvoll, ist für das Gate aber nicht nötig.


Hinweis zur Nachnutzung als Open Educational Resource (OER)

Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: “Übungsaufgaben zu Service-Schicht und Dependency Injection: Mitfahrgesuche einstellen und zurückziehen” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/spring-boot-service-dependency-injection-uebungsaufgaben veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/java-springboot/Backend. Erstellt mit KI-Assistenz; von mir umfassend redaktionell überarbeitet, geprüft und freigegeben. Stand: 11.11.2026.

[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: