Muss ich in TDD zuerst Test oder zuerst Interface schreiben?

23

Ich lerne TDD mit c #, soweit ich weiß , sollte Test die Entwicklung vorantreiben , dh erstmal einen Fehlertest schreiben schreiben, nachdem der Code mit dem absoluten Minimum geschrieben wurde , um den Test zu bestehen, und dann ein Refactoring durchführen.

Es heißt aber auch, dass " Program to Interface, nicht Implementation ", also schreibe zuerst ein Interface . Hier setzt meine Verwirrung ein. Wenn ich zuerst Interface schreibe, verstößt es gegen zwei Dinge

  1. Der für die Schnittstelle geschriebene Code wird nicht durch einen Test gesteuert .

  2. Es ist nicht das Nötigste offensichtlich kann ich es mit einer einfachen Klasse schreiben.

Soll ich anfangen, auch Tests für das Interface zu schreiben? Was soll ich ohne Implementierung testen?

Wenn diese Frage albern klingt, tut mir das leid, aber ich bin total verwirrt. Vielleicht nehme ich die Dinge zu wörtlich.

k4vin
quelle
8
"Programmieren an eine Schnittstelle" bedeutet, dass Sie das, was Sie benötigen, von einem Teil des Codes von der Art und Weise trennen, in der er ausgeführt wird. Es bedeutet nicht, buchstäblich ein interfacefür alles zu verwenden. A classbietet auch eine Schnittstelle, da Sie Implementierungsdetails in privateVariablen ausblenden können .
Doval
@Doval, ja, du brauchst nicht für alles ein Interface, nur was a heißt contract. Dies kann beispielsweise die Form einer abstrakten Klasse haben, sollte jedoch keine virtuelle Klasse / Methode sein, da Sie sie nicht instanziieren können sollten.
Trysis
2
TDD sagt: "Schreiben Sie einen Test, der fehlschlägt." Einige strenge TDDer sagen, dass es als "Fehler" gilt, wenn Sie den Test nicht kompilieren können , da der Datentyp, für den er ausgeführt wird, noch nicht deklariert wurde.
Solomon Slow

Antworten:

29

Ihre erste Verletzung ("Der für die Schnittstelle geschriebene Code wird nicht von einem Test gesteuert.") Ist ungültig. Nehmen wir ein einfaches Beispiel. Angenommen, Sie schreiben eine Taschenrechner-Klasse und schreiben eine Additionsoperation. Welchen Test könntest du schreiben?

public class CalculatorTest {
    @Test
    public void testAddTwoIntegers() {
        Calculator calc = new Calculator();
        int result = calc.add(2, 2)
        Assert.assertEquals(4, result);
    }
}

Ihr Test hat gerade die Schnittstelle definiert. Es ist die addMethode, sehen Sie? addNimmt zwei Argumente und gibt ihre Summe zurück. Sie können später feststellen, dass Sie mehrere Taschenrechner benötigen, und zu diesem Zeitpunkt eine (in diesem Fall) Java-Schnittstelle extrahieren. Ihre Tests sollten sich dann nicht ändern, da Sie das getestet haben öffentliche Schnittstelle dieser Klasse .

Auf einer theoretischeren Ebene sind Tests die ausführbare Spezifikation für ein System. Schnittstellen zu einem System sollten von den Benutzern dieses Systems gesteuert werden. Tests sind die erste Methode, mit der Sie Interaktionen definieren müssen.

Ich glaube nicht, dass Sie Interface-Design von Test-Design trennen können. Das Definieren von Interaktionen und das Entwerfen von Tests für diese sind dieselbe mentale Operation. Wenn ich diese Informationen an eine Schnittstelle sende, erwarte ich ein bestimmtes Ergebnis. Wenn etwas mit meiner Eingabe nicht stimmt, erwarte ich diesen Fehler. Sie können diese Entwurfsarbeit auf Papier machen und dann Ihre Tests daraus schreiben, oder Sie können sie gleichzeitig machen - es ist nicht wirklich wichtig.

Michael K
quelle
2
+1 " Ich glaube nicht, dass Sie Interface-Design von Test-Design trennen können " sollte fett
gedruckt sein
Es ist noch einfacher zu zeigen, dass Sie, wenn Sie mehr als eine Implementierung einer Funktionalität testen möchten, beispielsweise einen XML-Import und einen CSV-Import, diese mit genau derselben Methode über dieselbe Schnittstelle testen können, obwohl sich die Implementierung ändern wird. Darüber hinaus beinhalten Tests häufig einige Verspottungen, und für diese Schnittstelle sind sie erforderlich.
Walfrat
Sie sagen, der Test muss sich nicht ändern, aber new Calculator()stimmt die Implementierung? Wenn eine neue Implementierung erforderlich ist, führen Sie dann möglicherweise einen Multiplikationsrechner aus, und Sie müssen den zu verwendenden Test ändern, new AdditionCalculator()damit er weiterhin besteht. Oder vermisse ich etwas?
Steve Chamaillard
3
@SteveChamaillard Sicher, wenn Sie in Ihrem Design den Klassennamen von Calculator in AdditionCalculator ändern, muss der Test entsprechend geändert werden. Wenn Sie TDD durchführen, würden Sie natürlich zuerst den Test ändern, und der Klassenwechsel würde folgen, um den Test zu bestehen.
Eric King
5

Was machen wir, wenn wir eine schreiben interface? Schreiben wir Code oder entwerfen wir?

Ich bin kein Fan von Test Driven Design, aber ich liebe Test Driven Development . Persönlich habe ich meine besten Ergebnisse erzielt, wenn ich die Klasse im Voraus entworfen habe, indem ich die Schnittstelle entworfen habe, bevor ich einen Test schreibe. Ich zähle die Schnittstelle nicht als Code. Die Schnittstelle ist ein Design, das ich mit TDD implementieren werde. Es ist wahrscheinlich, dass sich eine Entwicklung ändert, während ich arbeite, aber es ist meine Roadmap (zusammen mit meiner Testliste).

Ich werde aufhören, bevor ich anfange zu schimpfen, aber hoffentlich ist das eine hilfreiche Art, darüber nachzudenken.

Badeente
quelle
4

Muss ich in TDD zuerst Test oder zuerst Interface schreiben?

Es hängt alles davon ab, wie orthodox / religiös Sie TDD machen möchten .

Ich lerne TDD

Da Sie gerade lernen, sollten Sie experimentieren, um einen persönlichen Workflow zu erhalten, der für Sie funktioniert.

  1. Wenn Sie es nach den Büchern machen wollen , schreiben Sie zuerst einen Test, der offensichtlich scheitern wird, weil Sie überhaupt nicht mit Code beginnen. Dann schreiben Sie einen Code, um den Test zu bestehen. In diesem Fall steht es Ihnen frei, den vorhandenen Code zu überarbeiten, da Sie einen Test haben, der eine Art Sicherheitsnetz für Überarbeitungen bietet. Die Entscheidung, ein Interface zu verwenden, ist eine Art Umgestaltung.

  2. Neben TDD oder nicht: Die Frage, ob eine Schnittstelle verwendet werden soll oder nicht, ist zunächst nicht interessant. Wenn Sie sicher sind, dass Sie ein unterschiedliches Verhalten haben, das Sie auf mehrere Objekte verteilen möchten, ist es natürlich sinnvoll, über die Verwendung einer Schnittstelle nachzudenken. Wenn Sie beispielsweise eine Ausgabe für verschiedene Ziele haben, ist es sinnvoll, diese über zu implementieren ein Interface Writer und haben unterschiedliche Klassen für die Ausgabe ( FileWriter , Printer etc.). Es ist zwar ein gängiges Sprichwort , an eine Schnittstelle zu schreiben , aber das bedeutet nicht: Verwenden Sie eine Schnittstelle für alles . Manchmal ist es eine Ebene der Indirektion zu viel. Btw. Gleiches gilt für Dienstleistungen. Aber das ist ein anderes Thema.

  3. Auf der anderen Seite könnten Sie testgetrieben auf eine andere Weise entwickeln: Entwerfen Sie Ihren Code für Testbarkeit. Das heißt, Sie schreiben Code, der einfach zu testen ist - obwohl Sie die Tests anschließend schreiben . Es spielt keine Rolle, ob Sie Tests vorher oder nachher schreiben, solange Sie sie testen.

Thomas Junk
quelle
5
Kann nicht mit dem letzten Punkt übereinstimmen "Es ist egal, ob Sie Tests vorher oder nachher schreiben, solange Sie sie trotzdem testen". Wenn Sie den Test später schreiben, können Sie nicht sicher sein, ob der Test das Richtige testet.
k4vin
4
Wie gesagt ... Es hängt davon ab, wie orthodox Sie sind ...
Thomas Junk
2

TDD oder BDD würde bedeuten, zuerst Ihre Domain-Interfaces zu bearbeiten und dann nach meiner Interpretation Tests gegen sie zu schreiben. Die Implementierung einer Schnittstelle weist ein erwartetes Verhalten auf.

Es ist immer noch ein Test vor Code, da eine Schnittstelle keine testbare Logik enthält. Dies ist die Struktur, für die Sie einen Test schreiben.

Ich würde es wie folgt machen

  1. Schreiben Sie das semi-formale Verhalten (Gegeben: Wann: Dann :)

  2. Schreiben Sie die Schnittstelle (um die Verkapselungsmethode für das Host-Verhalten zu verwenden).

  3. Schreiben Sie den Test, den es identifiziert (geben Sie das Gegebene ein, rufen Sie das Wann auf, testen Sie das Dann)

  4. Schreiben / Ändern Sie den Beton (Klasse, die die Schnittstelle implementiert), um den Test zu bestehen

user3721330
quelle
0

Schreiben Sie niemals Tests, bevor Sie die Schnittstellen entworfen haben. Wenn Sie darüber nachdenken, welche Arten von Tests zu schreiben sind (Testdesign), sollten Sie Ihre Anwendung nicht gleichzeitig entwerfen (Architektur). Denken Sie nicht gleichzeitig an zwei Dinge. Haben Sie von der Trennung von Bedenken gehört? Dies gilt nicht nur für die physische Struktur Ihres Codes, sondern auch für Ihren Denkprozess.

Entscheiden Sie zuerst, wie Ihre Anwendung gestaltet werden soll. Dies bedeutet, dass Sie Ihre Schnittstellen und die Beziehungen zwischen diesen Schnittstellen entwerfen. Bis Sie dies getan haben, sollten Sie nicht über Tests nachdenken. Sobald Sie wissen, was Ihre Schnittstellen sind, können Sie sie entweder zuerst erstellen und dann Tests gegen sie schreiben oder Tests zuerst schreiben und sie dann erstellen. In letzterem Fall können Sie die Tests natürlich nicht kompilieren. Ich sehe keinen Schaden oder Verstoß gegen die TDD-Philosophie in der Erstellung der Schnittstellen vor den Tests.

Nissim Levy
quelle
Die Argumentation in der oberen Antwort sieht überzeugender aus: "Ich glaube nicht, dass Sie das Interface-Design vom Test-Design trennen können. Das Definieren von Interaktionen und das Entwerfen von Tests für diese sind dieselbe mentale Operation - wann ich diese Informationen an ein Interface sende, erwarte ich ein bestimmtes Ergebnis . Wenn etwas mit meiner Eingabe falsch ist, ich erwarte diesen Fehler ...“
gnat
@gnat Ich glaube, Nissam bezieht sich hier auf das C # -Schlüsselwort interface, nicht auf den allgemeinen Begriff "Schnittstelle".
RubberDuck
@RubberDuck Ich denke auch, und ich glaube, das ist ein schlechter Ansatz. „Bis Sie dies getan haben , sollten Sie nicht denken über Tests beginnen ...“ Wie ich schrieb, Argumentation in Top-Antwort Aussehen zwingender, sowohl im allgemeinen Sinn der Schnittstelle und im Sinne konkreter Stichwort
gnat
Fair genug, @gnat, das war aus Ihrem Kommentar nicht klar. Persönlich stimme ich hier mit Nissam überein. Ich finde, es hindert die Leute daran, TDD als Vorwand zu benutzen, um überhaupt nicht zu entwerfen. YMMV.
RubberDuck
-2

Es ist in Ordnung, die Schnittstelle / den Code / den Test zur gleichen Zeit zu schreiben, solange ihre Einbindung in das Projekt atomar ist.

Es sei denn, Ihr Chef ist religiös in Bezug auf TDD. In diesem Fall müssen Sie wahrscheinlich eine leere Schnittstelle schreiben -> test -> minimaler Code (sinnloser Schritt) -> mehr Tests -> sinnloser Code -> mehr Tests -> endlich den echten Code schreiben - > fertig.

Alternative
quelle