Wie kann ich mit TDD beginnen, um einige einfache Funktionen zu codieren?


9

Ich habe im Grunde das Wesentliche von TDD. Ich bin verkauft, dass es nützlich ist und ich habe ein vernünftiges Kommando über das MSTEST-Framework. Bisher war ich jedoch nicht in der Lage, es als primäre Entwicklungsmethode zu verwenden. Meistens verwende ich es als Ersatz für das Schreiben von Konsolen-Apps als Testtreiber (mein traditioneller Ansatz).

Das Nützlichste daran ist für mich, wie es die Rolle von Regressionstests aufnimmt.

Ich habe noch nichts gebaut, das speziell verschiedene testbare Verhaltensweisen isoliert, was ein weiterer großer Teil des Bildes ist, das ich kenne.

Diese Frage besteht also darin, nach Hinweisen zu fragen, welche ersten Tests ich für die folgende Entwicklungsaufgabe schreiben könnte: Ich möchte Code erstellen, der die Aufgabenausführung in der Art von Produzent / Verbraucher kapselt.

Ich hörte auf und beschloss, diese Frage zu schreiben, nachdem ich diesen Code geschrieben hatte (ich fragte mich, ob ich TDD diesmal tatsächlich verwenden könnte).

Code:

interface ITask
{
    Guid TaskId { get; }
    bool IsComplete { get; }
    bool IsFailed { get; }
    bool IsRunning { get; }
}

interface ITaskContainer
{
    Guid AddTask(ICommand action);
}

interface ICommand
{
    string CommandName { get; }
    Dictionary<string, object> Parameters { get; }
    void Execute();
}

Sie sollten zuerst die Tests und dann die Schnittstellen geschrieben haben! Die ganze Idee ist, dass TDD für Ihre API ist.

1
Wie schreibt man Tests für noch nicht existierende Schnittstellen? Sie werden nicht einmal kompilieren.
Robert Harvey

5
Das ist dein erster fehlgeschlagener Test.
Cori

Antworten:


10

Beginnen Sie mit diesem Konzept:
1) Beginnen Sie mit dem gewünschten Verhalten. Schreiben Sie einen Test dafür. Siehe Test fehlgeschlagen.
2) Schreiben Sie genügend Code, um den Test zu bestehen. Alle Tests bestehen.
3) Suchen Sie nach redundantem / schlampigem Code -> Refactor. Siehe Tests noch bestanden. Gehe zu 1

Nehmen wir also auf # 1 an, Sie möchten einen neuen Befehl erstellen (ich strecke mich danach, wie der Befehl funktionieren würde, also nehmen Sie ihn mit). (Außerdem bin ich eher ein bisschen pragmatisch als extrem TDD)

Der neue Befehl heißt MakeMyLunch. Sie erstellen also zuerst einen Test, um ihn zu instanziieren und den Befehlsnamen abzurufen:

@Test
public void instantiateMakeMyLunch() {
   ICommand command = new MakeMyLunchCommand();
   assertEquals("makeMyLunch",command.getCommandName());
}

Dies schlägt fehl und zwingt Sie, die neue Befehlsklasse zu erstellen und ihren Namen zurückzugeben (Purist würde sagen, dies sind zwei Runden TDD, nicht 1). Sie erstellen also die Klasse und lassen sie die ICommand-Schnittstelle implementieren, einschließlich der Rückgabe des Befehlsnamens. Wenn Sie jetzt alle Tests ausführen, werden alle Tests angezeigt. Suchen Sie daher nach Refactoring-Möglichkeiten. Wahrscheinlich keine.

Als nächstes möchten Sie, dass es execute implementiert. Sie müssen sich also fragen: Woher weiß ich, dass "MakeMyLunch" erfolgreich "mein Mittagessen zubereitet" hat? Welche Änderungen im System aufgrund dieser Operation? Kann ich das testen?

Angenommen, es ist einfach zu testen auf:

@Test
public void checkThatMakeMyLunchIsSuccessful() {
   ICommand command = new MakeMyLunchCommand();
   command.execute();
   assertTrue( Lunch.isReady() );
}

In anderen Fällen ist dies schwieriger, und Sie möchten wirklich die Verantwortlichkeiten des zu testenden Probanden testen (MakeMyLunchCommand). Möglicherweise liegt die Verantwortung von MakeMyLunchCommand in der Interaktion mit Kühlschrank und Mikrowelle. Um es zu testen, können Sie einen Scheinkühlschrank und eine Scheinmikrowelle verwenden. [Zwei Beispiel-Mock-Frameworks sind Mockito und nMock oder schauen Sie hier .]

In diesem Fall würden Sie so etwas wie den folgenden Pseudocode tun:

@Test
public void checkThatMakeMyLunchIsSuccessful() {
   Fridge mockFridge = mock(Fridge);
   Microwave mockMicrowave = mock(Microwave);
   ICommand command = new MakeMyLunchCommand( mockFridge, mockMicrowave );
   command.execute();
   mockFramework.assertCalled( mockFridge.removeFood );
   mockFramework.assertCalled( microwave.turnon );
}

Der Purist sagt, testen Sie die Verantwortung Ihrer Klasse - ihre Interaktionen mit anderen Klassen (hat der Befehl den Kühlschrank geöffnet und die Mikrowelle eingeschaltet?).

Der Pragmatiker sagt Test für eine Gruppe von Klassen und Test für das Ergebnis (ist Ihr Mittagessen fertig?).

Finden Sie die richtige Balance, die für Ihr System funktioniert.

(Hinweis: Bedenken Sie, dass Sie möglicherweise zu früh zu Ihrer Schnittstellenstruktur gelangt sind. Vielleicht können Sie dies beim Schreiben Ihrer Komponententests und Implementierungen weiterentwickeln, und in Schritt 3 "bemerken" Sie die allgemeine Schnittstellenmöglichkeit.)


Wenn ich meine Benutzeroberfläche nicht vorab geschrieben hätte, welche Frage hätte zur Erstellung der Execute () -Methode geführt - einige meiner anfänglichen Versuche, TDD durchzuführen, sind ins Stocken geraten, als ich keinen "Schritt" hatte, um zusätzliche Funktionen zu stimulieren -, verstehe ich Das Gefühl, es gibt ein latentes Henne / Ei-Problem, das
umgangen

1
Gute Frage! Wenn der einzige Befehl, den Sie ausgeführt haben, "MakeMyLunchCommand" war, hat die Methode möglicherweise mit ".makeMyLunch ()" begonnen. Welches wäre in Ordnung gewesen. Dann machen Sie einen anderen Befehl ("NapCommand.takeNap ()"). Immer noch kein Problem mit verschiedenen Methoden. Dann beginnen Sie, es in Ihrem Ökosystem zu verwenden, wo Sie wahrscheinlich gezwungen sind, auf die ICommand-Schnittstelle zu verallgemeinern. Im Allgemeinen verzögern Sie die Verallgemeinerung oft bis zum letzten verantwortlichen Moment, weil YAGNI [ en.wikipedia.org/wiki/You_ain't_gonna_need_it ] =) In anderen Fällen wissen Sie, dass Sie dorthin gelangen, also beginnen Sie damit.
Jayraynet

(Auch hier wird davon ausgegangen, dass Sie eine moderne IDE verwenden, die das Refactoring von Dingen wie Methodennamen trivial macht)
jayraynet

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.