Schreiben Sie Unit-Tests für die ganze Zeit in TDD?


8

Ich habe lange Zeit Code im TDD-Stil entworfen und entwickelt. Was mich an TDD stört, ist das Schreiben von Tests für Code, der keine Geschäftslogik oder interessantes Verhalten enthält. Ich weiß, dass TDD mehr eine Designaktivität als ein Test ist, aber manchmal halte ich es für nutzlos, Tests in diesen Szenarien zu schreiben.

Zum Beispiel habe ich ein einfaches Szenario wie "Wenn der Benutzer auf die Schaltfläche" Überprüfen "klickt, sollte die Gültigkeit der Datei überprüft werden" . Für dieses Szenario schreibe ich normalerweise Tests für die Presenter / Controller-Klasse wie die folgende.

@Test
public void when_user_clicks_check_it_should_check_selected_file_validity(){
    MediaService service =mock(MediaService);
    View view =mock(View);

    when(view.getSelectedFile).thenReturns("c:\\Dir\\file.avi");

    MediaController controller =new MediaController(service,view);
    controller.check();

    verify(service).check("c:\\Dir\\file.avi");
}

Wie Sie sehen, gibt es keine Entwurfsentscheidung oder interessanten Code zur Überprüfung des Verhaltens. Ich teste Werte aus der an MediaService übergebenen Ansicht. Normalerweise schreibe ich, aber ich mag solche Tests nicht. Was tun Sie in diesen Situationen? Schreibst du die ganze Zeit Tests?

UPDATE:

Ich habe den Testnamen und den Code nach Beschwerden geändert. Einige Benutzer sagten, dass Sie Tests für solche trivialen Fälle schreiben sollten, damit in Zukunft jemand interessantes Verhalten hinzufügen könnte. Aber was ist mit "Code für heute, Design für morgen". ? Wenn jemand, einschließlich ich, in Zukunft interessanteren Code hinzufügt, kann der Test dafür erstellt werden. Warum sollte ich es jetzt für die trivialen Fälle tun?


1
"Einige Benutzer sagten, dass Sie Tests für solche trivialen Fälle schreiben sollten, damit in Zukunft jemand ein interessantes Verhalten hinzufügen kann. Aber was ist mit" Code für heute, Design für morgen ". ? "Geniale Widerlegung! +1
maple_shaft

Antworten:


5

Ich strebe keine 100% ige Codeabdeckung an. Und ich schreibe normalerweise keine Tests von Methoden, die offensichtlich keine Geschäftslogik und / oder mehr als ein paar Codezeilen enthalten. Aber ich schreibe immer noch Unit-Tests (mit TDD) von Methoden, die nicht so komplex erscheinen. Dies liegt hauptsächlich daran, dass ich den Komponententest bereits gerne habe, wenn ich Monate oder sogar Jahre später auf diesen Code zurückkomme, und ihn komplexer gestalten möchte. Es ist immer einfacher, vorhandene Tests zu erweitern, als alles von Grund auf neu zu erstellen. Wie Noufal sagte, ist es subjektiv. Mein Rat ist, die Tests zu schreiben, wenn Sie der Meinung sind, dass die Methode etwas komplex ist oder das Potenzial hat, komplexer zu werden.


2

Dies ist heute die zweite TDD-Frage, die Ärger über die Anzahl der zu schreibenden Tests vermittelt.

"Testen Sie es nur, wenn Sie möchten, dass es funktioniert."

Ich bin mir nicht sicher, ob ich den Test in der Frage verstehe.

Überprüfen Sie, ob Controller.Check () an den Dienst delegiert (Abhängigkeit), wobei das Argument der ausgewählte Dateiwert aus der Ansicht ist? Wenn ja, ist dies ein guter Test. Sie können den Controller ohne die eigentliche Service-Implementierung testen. (interaktionsbasierte Mikrotests).

Update: Jetzt, da mir klar ist, was Sie testen möchten, würde ich wahrscheinlich Code verschieben und einige Dinge umbenennen, sodass "Test Media Controller delegiert die Prüfung ausgewählter Dateien an den Mediendienst delegiert" angezeigt wird. - Dies ist eine gültige Spezifikation für die Steuerung.

public class TestMediaController

@Test
public void DelegatesSelectedFileCheckToMediaService(){
    string selectedMediaFileInView = "c:\\Dir\\file.avi";

    when(_view.getSelectedFile).thenReturns(selectedMediaFileInView);

    new MediaController(_service, _view).check();

    verify(_service).check(selectedMediaFileInView);
}

Ja. Es wird an die Serviceklasse delegiert.

Aber Frage: Lohnt es sich, diesen Test zu schreiben, um nur die Parameter zu überprüfen, die von der Ansicht an die Serviceklasse übergeben wurden?

@Ja - Es wird geprüft, ob check () korrekt verdrahtet ist. Es sollte einen weiteren Test geben, bei dem Service.Check () funktioniert, sobald der Anruf eingeht. Dieser Test sieht heute trivial aus, aber im Laufe der Zeit könnte jemand dieser Methode mehr Code hinzufügen (z. B. eine Schutzklausel, wenn eine Bedingung zurückgegeben wird;) und das vorhandene Verhalten unterbrechen. Dieser Test schützt davor.
Gishu

Ok, ich habe verstanden, aber was ist mit "Code für heute, Design für morgen". ? Wenn jemand oder ich in Zukunft interessanteren Code hinzufügen, kann ich dann einen Test dafür erstellen. Warum sollte ich es jetzt für die trivialen Fälle tun?

1
@mcaaltuntas: Der springende Punkt bei TDD ist, dass Sie die aktuelle Spezifikation testen. Wenn also eine zukünftige Codeänderung nicht mehr der aktuellen Spezifikation entspricht, erfahren Sie davon. Unabhängig davon, wie einfach es Ihrer Meinung nach ist, die Spezifikation jetzt zu implementieren, könnte jemand in Zukunft den Code ändern, ohne auf diese spezielle Anforderung zu achten. In diesem Fall könnte er sie brechen und nicht erkennen, dass er einen Test dafür hinzufügen muss . Wenn Sie den Test jetzt nicht schreiben, machen Sie kein TDD. Wenn Sie nicht die gesamte Spezifikation testen möchten, ist dies Ihr Aufruf, aber Sie wenden TDD nicht auf diesen Teil an.
Steve Jessop

1

Ich würde so einen Test nicht schreiben (oder zumindest würde ich ihn nicht so nennen). Stattdessen würde ich einen Test für die Funktion schreiben, für die dieser Aufruf erforderlich ist. check()Wenn diese Überprüfung oder eine gleichwertige Aktion nicht durchgeführt wird, funktioniert die übergeordnete Funktion nicht. Warum muss Ihr Code die check()Methode aufrufen ?

Im Allgemeinen versuche ich, die Tests von den Implementierungsdetails entkoppelt zu halten, sodass zumindest der Name des Tests nur über die vom Objekt bereitgestellten externen Funktionen spricht. Implementierungsdetails wie Objekte und Methoden werden im Testnamen nicht erwähnt.

Dies erleichtert das Refactoring (es sollte nicht erforderlich sein, Tests zu ändern, wenn Sie die Implementierung ändern), und es erleichtert auch, herauszufinden, ob ein Test veraltet ist (die angegebene Funktion wird nicht mehr benötigt). . Dies erleichtert auch das Erkennen von unnötigem / totem Code, da Low-Level-Boilerplate (wie Getter und Setter) nur hinzugefügt / aufbewahrt werden, wenn sie für übergeordnete Funktionen erforderlich sind.


Ok. Sie haben Recht mit der Benennung. Sie können sich den Namen vorstellen, "wenn der Benutzer auf" Überprüfen "klickt, sollte er die Eigenschaften der ausgewählten Datei überprüfen". Aber die Frage ist nicht über die Benennung Schreiben Sie Tests für "offensichtlichen" Code oder nicht

Ich habe den Testnamen geändert, aber die Frage bleibt noch offen.

Was soll das System tun, wenn diese Prüfung fehlschlägt? Eine Ausnahme werfen oder etwas anderes? Ich würde wahrscheinlich zwei Tests schreiben: Was passiert, wenn die Datei gültig ist und wenn sie ungültig ist? Wenn es viele Möglichkeiten gibt, wie eine Datei ungültig sein kann, würde ich diese Tests direkt gegen MediaService schreiben.
Esko Luontola

Ja Probaby, ich würde auch zwei Tests schreiben, aber nicht gegen die Controller-Klasse. Ich würde sie gegen die MediaService-Klasse schreiben (wie Sie sagten). Der Test überprüft also nur, ob die Serviceklasse mit Parametern aus der Ansicht aufgerufen wurde. Denken Sie, dass es sich lohnt, diesen Test zu schreiben, um nur Argumente zu überprüfen, die von der Ansicht an die Serviceklasse übergeben wurden?

"Denken Sie, dass es sich lohnt, diesen Test zu schreiben, um nur die Argumente zu überprüfen, die von der Ansicht an die Serviceklasse übergeben wurden?" Es hängt davon ab, ob. Wenn End-to-End-Tests vorhanden sind, die sicherstellen, dass die Prüfung funktioniert (dh eine Fehlermeldung oder etwas, wenn die Datei nicht gültig ist), reicht es für die Komponententests des Controllers möglicherweise aus, nur die Methode zu überprüfen wird genannt; Es würde beim Refactoring einen schnelleren Schutz bieten als die End-to-End-Tests.
Esko Luontola

0

Das ist subjektiv. Ich mache nicht immer TDD, aber wenn ich das mache, versuche ich, die Codeabdeckung als meine Metrik dafür beizubehalten, ob meine Tests umfassend sind oder nicht. Manchmal werde ich faul und überspringe einfach Teile, die mir "offensichtlich" erscheinen. Manchmal verletze ich den Rot-, Grün- und Refaktor-Zyklus und schreibe mehr Code als nötig, aber im Laufe der Zeit bin ich in einen Rhythmus geraten, mit dem ich mich nicht wohl fühle.


0

Interaktionen zwischen Klassen wie den oben genannten und dem einfacheren Original, für das ich einen Test schreiben würde. Interaktionen können im Laufe der Zeit komplexer werden, daher ist es eine gute Sache, die Grundlagen zu schaffen.


0

Wenn jemand oder ich in Zukunft interessanteren Code hinzufügen, kann ich dann einen Test dafür erstellen. Warum sollte ich es jetzt für die trivialen Fälle tun?

Sie gehen davon aus, dass in Zukunft jemand weiß, dass dieses UI-Element vorhanden ist und wie es im Backend aufgerufen wird.

Mein aktuelles Projekt hat über 30 Entwickler in sechs verschiedenen Teams innerhalb derselben Codebasis. Triviale UI-Ergänzungen verschwinden ständig im Nebel. Niemand wird später zurückgehen und einen Testfall hinzufügen, weil sich niemand daran erinnern wird, dass er da ist, und wenn er bricht, lautet die Forderung: "Warum haben sie keinen Test geschrieben? Es wäre so einfach gewesen!"


0

Wie gewöhnlich...

Es hängt davon ab, ob

Es ist schwierig, die Nützlichkeit dieses speziellen Tests in einem TDD-Kontext zu erkennen, da wir die Geschichte nicht kennen.

Wenn die Geschichte lautet Als [Medienbenutzer] möchte ich [die Gültigkeit der Medien überprüfen können], damit [ich weiß, wenn eine Datei nicht verfügbar ist]

dann das Szenario Gegeben [eine Schaltfläche zum Überprüfen des Mediums] Wenn [der Benutzer auf die Schaltfläche klickt], dann [wird die Gültigkeit der Datei überprüft]

macht Sinn. Es ist trivial, aber es macht Sinn.

Wenn die übergreifende Geschichte größer ist, ist die Szenariodefinition möglicherweise zu eng.

Merken:

TDD! = Unit Testing

TDD testet Funktionen . Wenn es sich um eine Funktion handelt, verdient sie einen Test, um sie zu überprüfen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.