Bewertung, ob zuerst Unit-Tests oder Integrationstests für Blue-Sky- / Prototyp-Projekte geschrieben werden sollen

10

Was mir kürzlich aufgefallen ist, ist, wenn ich die folgenden Arten von Projekten mache:

  • Zu Beginn eines Projekts
  • Arbeiten an einem MVP / Prototyp
  • Hinzufügen von Funktionen, die nicht vollständig definiert sind
  • Arbeiten an einem kleineren Projekt

Als Referenz arbeite ich gerade an einem Python-Projekt, das derzeit ~ 1k Codezeilen enthält, einschließlich einiger Kommentare und aller Leerzeichen.

Ich finde es immens einfacher, zuerst Integrationstests zu schreiben, am Code zu arbeiten und dann, sobald die API etwas gehärtet ist, tatsächlich Unit-Tests hinzuzufügen. Die Arten von Tests, die ich mainsozusagen für meine Funktion ausführen kann und die mehr "Ende-zu-Ende" sind als alles andere.

Dies liegt daran, dass Komponententests wirklich ärgerlich sind, wenn sich eine API relativ schnell ändert, was häufig der Fall ist, wenn an einem Projekt gearbeitet wird, das einem oder den meisten der oben genannten Kriterien entspricht.

Ist dieser Ansatz ein guter Ansatz und welche Kriterien sollten bei der Entscheidung berücksichtigt werden, ob zuerst mit Unit- oder Integrationstests für diese Art von Projekten begonnen werden soll? Vermisse ich den Wert von Unit-Tests dieser Art von Projekten, bevor die APIs fester werden?

Enderland
quelle
6
Tun Sie, was für Sie am besten funktioniert. Hören Sie nicht auf Leute, die sagen, dass Sie auf eine bestimmte Weise arbeiten müssen, um effizient zu sein: Sie wissen, wann Sie effizient sind und wann nicht. Es spielt keine Rolle, ob Sie zuerst die Integrationstests oder zuerst die Komponententests schreiben. Bei einigen Projekten kann ein Weg einfacher sein, bei anderen der andere. Was Sie beschreiben, kann der Unterschied zwischen Top-Down- und Bottom-Up-Design sein. Beide sind nützlich, aber von oben nach unten ergeben sich normalerweise bessere Designs.
Frank Hileman
@ FrankHileman in der Tat, das ist mein Ansatz. Aber da ich neugierig bin, möchte ich sicherstellen, dass ich den richtigen Ansatz mache, falls mir etwas fehlt.
Enderland
Konzentrieren Sie sich zuerst auf die Spezifikationen: die Nicht-Code-Teile. Was sind die Invarianten des Systems? Dabei müssen Sie möglicherweise zuerst die niedrige oder zuerst die hohe Ebene herausfinden. Es hängt davon ab, wo sich die kritischsten oder riskantesten Algorithmen befinden. Versuchen Sie zuerst, diese aus dem Weg zu räumen. Das ist grundlegendes Risikomanagement.
Frank Hileman
1
Wenn Sie an einem Prototyp arbeiten, ist es in Ordnung, überhaupt keine Tests zu schreiben. Ziel des Prototyps ist es, die Arbeitsidee zu überprüfen. Die Implementierung eines Prototyps hilft dabei, das erwartete Design der Anwendung zu erkennen.
Fabio
Es heißt Outside-In-Entwicklung. Vielleicht möchten Sie das folgende Buch
lesen

Antworten:

7

Vermisse ich den Wert von Unit-Tests dieser Art von Projekten, bevor die APIs fester werden?

Nein, es geht dir gut.

Die beiden großen Ziele von TDD sind:

  • Definieren von Schnittstellen nach tatsächlicher Nutzung und nicht nach interner Implementierung 1
  • Maximierung der Testabdeckung

Die Testabdeckung kann in beiden Fällen ziemlich gut maximiert werden. Das heißt, unabhängig davon, ob Sie zuerst kleine , isolierte Einheiten oder große , "integrierte" Einheiten testen , haben Sie die Möglichkeit, Ihre Tests vor Ihren Implementierungen zu schreiben.

Wenn Sie dabei zuerst Tests auf höherer Ebene ("Integration") schreiben, erhalten Sie die Gewissheit, dass Ihre Schnittstellen und Interaktionen auf höherer Ebene auch in erster Linie nach ihrer Verwendung und nicht nach ihren internen Implementierungen definiert werden.

Der gleiche Effekt kann größtenteils mit einigen guten "Architekturen" und Diagrammen erzielt werden. Diese Tests auf hoher Ebene können jedoch häufig Dinge aufdecken, die Sie in Ihren Diagrammen übersehen haben - oder die Sie bei Ihrer "Architektur" -Arbeit einfach falsch gemacht haben .


Wenn Sie TDD (oder ähnliches) nicht durchführen, spielt die Reihenfolge, in der Sie die Tests schreiben, keine große Rolle. Die Schnittstellen sind zum Zeitpunkt des Tests bereits vorhanden, sodass es weitaus weniger wahrscheinlich ist, dass Ihre Tests etwas ändern. Sie dienen nur zum Schutz vor bestimmten Änderungen.

Wenn Sie jedoch die Implementierung von oben nach unten oder von oben nach oben erstellen möchten, gilt der erste Punkt immer noch in hohem Maße. Der High-Level-Code hilft beim Definieren von Low-Level-Schnittstellen. Wenn die Low-Level-Schnittstellen zuerst geschrieben werden (oder anderweitig bereits vorhanden sind), ist der High-Level-Code ihrer Gnade ausgeliefert ...


1. Dies gilt auch dann, wenn Sie kein Full-On-TDD durchführen. Selbst wenn Sie vor Ihrer Implementierung nur 1 oder 2 Tests schreiben , können diese 1 oder 2 Tests Ihnen helfen, Ihre Schnittstellen zu definieren oder zu verfeinern, bevor es zu spät ist!

svidgen
quelle
1

Ich habe so gearbeitet, wie du arbeitest. Und ich werde dir nicht sagen, dass du es nicht kannst. Ich werde Sie vor etwas warnen, auf das Sie stoßen können.

Wenn jeder Unit-Test nur eine Nachrüstung ist, ist es schwer zu lernen, sie flexibel zu gestalten. Sie sind in der Regel nichts anderes als Regressionstests. Da Sie sie nie als Refactor verwendet haben, ist es sehr einfach, die Arten von Tests zu schreiben, die das Refactoring tatsächlich erschweren. Dies neigt dazu, außer Kontrolle zu geraten, bis Sie jegliches Vertrauen in TDD verlieren.

Sie arbeiten jedoch bereits an etwas. Ich werde dir nicht sagen, dass du aufhören sollst. Ich werde sagen, es könnte sich lohnen, etwas anderes zu beginnen, für das Sie Zeit haben, den rot-grünen Refaktor-Zyklus von Anfang an zu erkunden und zu verfolgen. Stellen Sie sicher, dass Sie die Tests tatsächlich verwenden, um die Umgestaltung zu unterstützen. Bis Sie diese Arbeitsweise beherrschen, verwenden Sie sie sparsam für etwas, das wichtig ist. Dies ist eine ganz andere Art des Codierens und gewöhnungsbedürftig. Es auf halbem Weg zu tun, wird niemandem nützen.

Das gesagt

Ich finde es immens einfacher, zuerst Integrationstests zu schreiben, am Code zu arbeiten und dann, sobald die API etwas gehärtet ist, tatsächlich Unit-Tests hinzuzufügen. Die Arten von Tests, die ich sozusagen für meine Hauptfunktion ausführen kann und die mehr "Ende-zu-Ende" sind als alles andere.

Verstehen Sie, dass ein Komponententest NICHT einfach ein Test ist, der auf eine Klasse angewendet wird. Solange die API, an der Sie arbeiten, getestet werden kann, ohne eine der folgenden Aktionen auszuführen, führen Sie Unit-Tests problemlos durch :

  • Es spricht mit der Datenbank
  • Es kommuniziert über das Netzwerk
  • Es berührt das Dateisystem
  • Es kann nicht gleichzeitig mit einem Ihrer anderen Komponententests ausgeführt werden
  • Sie müssen spezielle Dinge an Ihrer Umgebung tun (z. B. das Bearbeiten von Konfigurationsdateien), um sie auszuführen.

Michael Feathers: Eine Reihe von Unit-Testing-Regeln

Wenn Ihr End-to-End-Test also mehr als ein Objekt umfasst, ist das in Ordnung. Dies ist Unit-Test, kein Objekttest.

So wie private Methoden nicht mehr getestet werden müssen, als sie durch Testen der öffentlichen Methoden, die sie verwenden, getestet werden, müssen Objekte zunächst nicht unter ihrem eigenen Testgeschirr entwickelt werden. Nur wenn Objekte für die Verwendung unabhängig von der End-to-End-Story in Betracht gezogen werden, müssen sie wirklich so behandelt werden, als hätten sie ihre eigene Schnittstelle und ihr eigenes Verhalten, um dies zu bestätigen. Wenn Sie vorsichtig sind, machen Sie diese Objekte öffentlich. Auf diese Weise machen Sie keine Versprechungen, die Sie nicht getestet haben.

candied_orange
quelle