Man könnte vielleicht einen eigenen Validator (oder was auch immer da das passende ist) bereitstellen, den Methodeaufruf ausführen und prüfen, dass die entsprechenden Methoden aufgerufen wurden, zB mit Mockito Spys.
Aber keine Ahnung ob’s funktioniert, der Weg von @Apple’s Jünger sollte in jedem Fall klappen, und man hat dann auch direkt die Validierung mitgetestet.
Der normale Lebenszyklus würde je nachdem, welche Validierungsframework eingesetzt wird, beim ausführen eines Requests oder der Persistierung aufgerufen werden.
Du müsstest also den Dienst (Controller oder Repo) mocken und eine konkrete Implementierung eines DTO/ POJO/REC übergeben.
Also erst einmal den Ansatz beim Testen: Du testest ja lediglich Deinen Code. Und im Rahmen der Spring Boot Validation wird das vermutlich JRS 380 sein und Du hast eine @Validate Annotation wie in Validation in Spring Boot | Baeldung beschrieben?
Dann ist der Test doch lediglich, dass nicht valide Übergaben nicht übernommen werden. Und genau das wird durch die Tests, die in #2 beschrieben werden, auch getestet.
Dass Die Validierung selbst richtig ist, ist da nicht der Code, den Du testest. Unit Tests testen immer nur die Unit!
Das Andere wäre, dass Du die Entity, die irgendwelche Validierungs-Annotations hat, testen musst. Das ist aber dann ein Test der Entity und nicht der Test von irgendwelchen anderen Stellen. Und das kannst Du ja in dem Test für die Entity machen mit etwas wie:
Java:
privateValidator validator;@BeforeEachpublicvoidbefore(){
validator =Validation.buildDefaultValidatorFactory().getValidator();}@TestpublicvoidtestEntityNotValid(){MyEntity entiy =......;// Create not valid EntitySet<ConstraintViolation<MyEntity>> actual = validator.validate(entity);assertThat(actual.iterator().next().getMessage()).isEqualTo("the given error message");}
Das einfach nur mal um es zu skizzieren.
Also der wichtige Punkt, den ich sehe: Überlege Dir, was Du wo testen willst. Und vermische es nicht! Das macht es nur komplexer als es sein muss wie dann die Idee von #5 zeigt (Ja, interessanter Weg @Apple’s Jünger - etwas in der Art würde theoretisch wohl gehen).
Das ist übrigens auch der Vorteil von TDD. Da kommst Du gar nicht erst zu diesen abstrusen Konstrukten! Du versuchst nicht erst, Dinge sonst wo zu testen, wo es nicht hin gehört und man schreibt nicht erst irgend welchen Code, der nicht testbar ist (hier jetzt weniger gegeben). Bei Dir wäre die Anforderung: Es gibt eine Entity und die hat diese Vorgaben bei der Validierung: ......
Und das setzt Du dann um indem Du Tests schreibst. Da gibt es also keinen Controller oder Repo oder sonst etwas. Da gibt es nur die Entity mit den Vorgaben und da sind die Annotations und diese testest Du.
(Und damit ist das auch ein super Ort, das mit Kommentaren zu versehen: Wieso gibt es diese fachlichen Anforderungen? Wenn Jemand sich dafür interessiert, dann schaut er den Test an und sieht: Da darf keine negative Zahl sein weil .... ==> Unit Tests als Dokumentation des Spezifikation. Deutlich einfacher als das separat schreiben zu wollen - was dann am Ende statt findet: "Wir müssen morgen liefern - schreib doch mal schnell die Spezifikation bis heute Abend." und dann kopiert man auf irgendwelchen Dokumenten schnell was zusammen und vergisst irgendwelche wichtigen Emails oder Jira Tickets oder oder oder ... Kommt das bekannt vor? )
Also mein Punkt wieso ich den aufruf der Validierung testen wollte ist folgender:
Der Controller soll nur Valide Objekte DTOs entgegen nehmen. Wie ein Valides DTO aussieht ist ja im DTO beschrieben. Der Controller prüft meines Verständnis nach nicht die Validierung, sondern gibt diese Validierung weiter. Wenn ich damit richtig liege, sollte in einem Unit Test des Controllers, nicht die Regeln der Validierung geprüft werden sondern ob diese stattfindet.
Die Validierungs Regel des Dto kann sich ja mal ändern, wieso muss ich dann den Controller Test anpassen.
ZB die post Methode ist so spezifiziert:
Darf nur valide request entgegen nehmen
Wenn invalides Request gibt er 400 mit den Validierungs verstossen zurück
Gibt den Request an den Service weiter
Wirft der Service eine Exception, gibt der Controller entsprechenden Status zurück
wenn der Service erfolgreich das Objekt verarbeitet hat gibt er einen 200 mit dem Ergebniss zurück
Sollte ich nicht genau das und nur das im Controller testen?
ok, dann haben wir die gleiche Sichtweise. Aber für Tests gilt ja durchaus auch das KISS Prinzip: keep it simple, stupid.
Das Problem, das due vermutlich siehst, ist: du brauchst ein nicht valides DTO für den Test. Und das gehört nicht wirklich in den Test des Controllers.
So aus dem Stehgreif würde ich sagen: Du hast ja ein DTOTest zum Test der Validierungen. Da kann also eine public static DTO createInvalidDTO Methode oder so sein und die wird dann aufgerufen.
(Wobei ich sowas gerne in Basis Klassen packe und dann so Methoden vererbe. Aber ob das hier passt, weiß ich nicht. Der Vorteil ist, dass die Erstellung der Instanzen dann so in allen Tests ist und die Abhängigkeit sauber ist. Da werden halt teilweise sogar eine Art Testumgebung aufgebaut für Business Tests …. Das ist teilweise einfacher als wirklich alles knallhart zu mocken - aber das ist eine Form der „Schlamperei“ daher das auf keinen Fall als Best Practice sehen!)
Ob eine Validierung aufgerufen wird, ist da nicht spezifiziert, nur wie auf valide und invalide Objekte reagiert wird. Je nachdem wie du den Controller aufbaust, kann man das auch ganze auch erstmal in einem Unit Test ohne durchgeführte Validierung testen (und die dann eben in einen Test auf höherer Ebene auslagern).