Loading...

A "Unit" In A Test Is Not The Class Under Test

5. Dezember 2023
2 Minuten Lesezeit
Beitrag teilen:

Hi,

Was ist eigentlich eine “Unit” im Unit-Test?

Dumme Frage, sagst du dir vielleicht.

Aber sie ist interessant. Über viele Jahre war ich, wie die meisten die über das Thema sprechen, der Meinung:

Eine Unit ist eine Klasse, die ich testen möchte.

Und vielleicht hast du das so auch gesehen. Und du hast mit Sicherheit damit ein paar Probleme gehabt.

Zum Beispiel, die Symptome des daraus resultierendem Coupling

Denn was passiert, wenn wir eine UserServiceTest Klasse schreiben? Wir haben - ohne es zu merken - ein semantisches Coupling zwischen der UserService Klasse und dem UserServiceTest hergestellt.

Wenn wir den UserService umbenennen, dann müssen wir auch den UserServiceTest umbenennen. Wie oft ich das schon vergessen habe.

Noch vor drei Jahren schrieb ich den Blog “Best Practices For Unit Tests” .

In diesem empfahl ich in Java die Methodensignatur in der Form methodName_conditions_expectedResult zu schreiben.

Gleiches Problem. Eine semantische Kopplung zwischen dem Test und meiner Implementierung. Eine Umbenennung der Methode sorgt für eine Umbenennung der Testfunktion. Oder - wahrscheinlicher - für Verwirrung, weil ich es vergesse.

Im gleichen Blog beschrieb ich noch ein anderes Muster, dass ich heute als Anti-Pattern empfinde.

Ich empfehle (fast) alles zu mocken .

Doch, wenn wir alles mocken, dann spiegeln wir nur die Implementierung - mit etwas anderen Worten.

Im ersten Satz dieses Absatz spreche ich aus, was heute die meisten Entwickler leben:

A unit test - by definition - tests only, and exclusively, the unit under test.

Aber genau hier liegt das Problem. Die Definition ist falsch.

In der ursprünglichen Definition war mit “Unit” die Unabhängigkeit des Tests selbst gemeint.

Es war ein weitverbreitetes Problem, dass Tests aufeinander aufbauten. Dieser Begriff sollte klar machen, dass ein Unit-Test unabhängig lauffähig ist.

Alle Probleme, die ich beschrieben habe, sorgen für eine starke Kopplung zwischen den Implementierungsdetails und dem Test. Doch dabei wollen wir gar nicht die Implementierung testen.

Wir wollen ein Verhalten testen.

Und darauf musst du dich konzentrieren. Statt isEmpty_emptyList_true möchtest du einen listWithNoElementsIsEmpty Test.

Hast du gemerkt, wie sich das Mindset verändert? Ich habe das Verhalten einer Liste beschrieben. Es wird nicht mehr die API getestet.

Wenn wir uns von der Implementierung trennen, dann wird alles einfacher.

Tests sagen uns, welches Verhalten nicht mehr funktioniert. Ein Refactoring sorgt nicht mehr automatisch zu vielen false positives. Wir schreiben weniger Tests. Decken aber das gleiche ab.

Probiere das doch für dein nächstes Feature aus 🙂

Rule the Backend,

~ Marcus

Top