Laden...

Test Driven Development: Planung, Workflow, Vorgehensweise. Wie entwickle ich mit TDD?

Erstellt von Solix0x vor 5 Jahren Letzter Beitrag vor 5 Jahren 1.438 Views
S
Solix0x Themenstarter:in
12 Beiträge seit 2018
vor 5 Jahren
Test Driven Development: Planung, Workflow, Vorgehensweise. Wie entwickle ich mit TDD?

Guten Tag,

ich habe bei 2 Projekten von mir gemerkt, dass ab einem gewissen Entwicklungsstand Unit-Tests sehr nützlich gewesen wären und deshalb möchte ich bei meinen nächsten Projekten diese auch mit Hilfe von TDD einsetzen.

Nur kommt bei diesem Ansatz ein Problem auf. Angenommen, ich möchte ein einfaches Konsolenprogramm schreiben, dass das Ergebnis einfacher Rechnungen, die als String eingeben werden wie zum Beispiel "3 + 5 * (4 - 5 * 6)".

Wenn ich naiv versuchen würde das Programm zu schreiben, dann würde das Resultat eine Funktion sein, die einen String als Parameter hat und einen Int zurückgibt. Nach TDD würde ich zuerst den Test schreiben. Wohlgemerkt darf nach TDD kein weiterer Test geschrieben werden, bis der aktuelle, der noch fehlschlägt, grün durchläuft.

Doch wenn man für diese Funktionalität, die noch größer sein könnte, nur eine einzige Funktion schreibt und nicht in weitere Funktionen unterteilt, dann kann der Code schnell unübersichtlich werden.

Alternativ könnte ich mir vorher überlegen, was für Funktionen ich brauche und schreibe zuerst Tests für diese. Aber kann bzw. sollte man ein Programm, dass auch mal sehr groß werden kann, wirklich so gut durchplanen? Irgendwie muss dann ja so ein Plan vorhanden sein, denn wenn ich nicht nur eine Funktion schreiben will, dann muss ich ja auch Tests für andere Funktionen schreiben und auch so, dass diese zur Funktionalität beitragen. Ergo muss man (denke ich) sich einen Plan machen, denn man kann ja nicht aus dem Kopf Tests und Funktionen schreiben und hoffen, dass diese zur Problemlösung beitragen.

Aber wenn ich ein Programm schreiben will, dass langfristig maintained werden soll, dann kommen auch während der Entwicklung Anderungen hinzu, die im Plan anfangs nicht dabei waren.

Dadurch ergibt sich für mich ein Widerspruch und ich weiß nicht, wie ich vorgehen soll.

Die Frage ist: Wie implementiere ich TDD so in die Entwicklung, dass ich die eben beschriebenen Probleme löse bzw. wie ist die Entwicklung mit TDD gedacht? (vermutlich anders, als ich noch denke)

16.827 Beiträge seit 2008
vor 5 Jahren

Ich bin kein Fan von hartem TDD. TDD und die Menge an Tests sind natürlich auch extrem teuer; weil sehr aufwändig.

Das lohnt sich daher vor allem für Software(teile); die nahezu eine vollständige Testabdeckung benötigt..
Ich kenne aber keine Plattform, die im gesamten nach TDD entwickelt wird - weil eben viel zu teuer.

Aber wenn ich ein Programm schreiben will, dass langfristig maintained werden soll, dann kommen auch während der Entwicklung Anderungen hinzu, die im Plan anfangs nicht dabei waren.

Und deswegen wird TDD auch in der Härte nicht vollständig angewandt; sondern zB. wenn man einen Parser oder Algorithmus benötigt eben ein solches Teilprojekt dann mit TDD umgesetzt, weil es zB. eine extrem wichtige Kernkomponente ist.
Wir verwenden auch TDD - aber eben genau für solche Funktionen und nicht für ein ganzes Projekt.

Wenn man ein wenig in der Konferenzwelt unterwegs ist, dann siehst Du, dass TDD oft in der Finanzberechnung anwendung findet.
zB Versicherungsvergleiche bekommen genaue Vorgaben, was ein Berechnung A rauskommen muss: ein Paradebeispiel für TDD: Du hast Eingang und Ausgang.

TDD ist absolut keine Lösung, dass Du keine Probleme mehr hast. TDD macht durchaus auch Dinge viel komplexer und aufwändiger als eigentlich notwendig.
TDD ist aber durchaus ein Werkzeug des modernen Fail-Fast Prinzips; aber nicht das einzige.

Schau Dir ein paar Videos, Pro/Cons zu TDD an und entscheide dann, ob TDD wirklich das ist, was Du benötigst.

P
1.090 Beiträge seit 2011
vor 5 Jahren

Hi Solix0x,

bei TDD schreibst du ja Test für Methoden die Public sind. Du kannst Teile des Codes natürlich auch in private Methoden auslagern. Das sollte in der Refactoring Phase passiert. Vereinfacht. Test schlägt Fehl -> Test bestanden -> Refactoring Test funktioniert immer noch. Nächster Test für die Methode.
Wenn es nötig ist kannst du die private Methode auch beim Refactoring in eine eigene Klasse auslagern und public machen. Deine Tests sollten immer noch Funktionieren und du kannst, dann wenn es nötig ist für die Methode Tests schreiben.

Änderungen sind ja eigentlich im Normalen Workflow schon vorgesehen. Du fängst ja möglich einfach an. Ich denke mal anstelle einer Zahl als String übergibst du "NULL" , "STring.Empty" oder eine andere ungültige Eingabe. Ich würde hier eine Exception erwarten. Dann kommt der nächste Test z.B. "1" ich würde erwarten. Das mir die Methode dann die Zahl zurück gibt. (Negative Test auf Max und Min nicht Vergessen). Und dann die Addition "5+3". Vielleicht brauchst du ja erst mal die Multiplikation nicht und musst sie erst Jahre später Implementieren. Dann schreibst du die neuen Test und implementierst die Änderungen. Die alten Test sollten in dem Fall noch weiter Funktionieren.

Es kann natürlich auch sein, das Änderungen alte Test brechen. Weil es so gewollt ist. Dann spricht nichts dagegen den Test auch zu ändern. Z.B. könnte "*" im String erst mal eine "Not Supported" Exception schmeißen. Was sich dann ändert, wenn es nach Änderungen doch funktionieren soll.

Der Punkt hier ist, das du drauf hingewiesen wird. Das sich was geändert hat und du dann zustimmst, das du die Änderung auch möchtest.

Und ja es wird manchmal als Nachteil Bezeichnet, das wenn man Breaking Changes macht. Noch den Aufwand hat etliche Tests anzupassen. Der Vorteil ist, es kann dir nicht aus Versehen geschehen.

Das eigendliche Buch von Kent Beck zu TDD finde ich persönlich auch nicht zu gut. Ganz nett für die Grundlagen, aber ich denke nicht wirklich für den Alltag brauchbar. Amszon:Effektives Arbeiten mit Legacy Code und Amazon:The Art Of Unit Testing finde ich da schon besser.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

16.827 Beiträge seit 2008
vor 5 Jahren

bei TDD schreibst du ja Test für Methoden die Public sind.

.. das hab ich vergessen; da bin ich gar nicht drauf eingegangen.

Zumindest in .NET würde man jegliche Schnittstelle in TDD durch ein Interface abdecken.
Die jeweilige Implementierung würde man dann gegen das Interface fahren lassen.

Der Test läuft dann gegen das Interface* - und nur indirekt gegen die Implementierung.

// Test: 
IPersonRepository rep = new InMemoryPersonRepository...

// test nun gegen rep
P
1.090 Beiträge seit 2011
vor 5 Jahren

Bin eigentlich noch dabei, eine Ergänzung zu meinen 1. Beitrag zu schreiben und habe eigentlich keine Lust mit Abt zu diskutier. (Ich werde es auch nicht machen.)

Aber Nein ich kann kein Interface mit Unit Tests testen (auch nicht Interface*), sondern nur Konkrete Implementierungen. Ein Interface MakeSound, was für eine Implementierung Katze „Miau“ und für einen Hund „Wuff“ zurück gibt. Kann nicht als Interface getestet werden. Es gibt natürlich Fälle in denen ich die Gleichen Werte in eine Methode gebe und dann die Gleiche Rückgabe erwarte. Grundlegen sollten die Implementierung der Methoden was anders machen. Sonst bräuchte ich nicht die 2. Implementierung, der Methode. Und dann sollte ich sie Unterschiede Testen.

Interfaces sind dann Interessant, wenn ich sie durch Mocks (Ich ersetze die Konkrete Implementierung durch eine Klasse, die mir einen Vorgegebenen Wert zurück gibt) bei Integrationstests ersetze kann. Und so die Tests Vereinfachen kann. Um sie z.B. von einer DB abzukoppeln.

Da sind wir aber eher bei Integrationstests.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

P
1.090 Beiträge seit 2011
vor 5 Jahren

Ergänzend vielleicht noch.

Wenn ich es richtig im Kopf habe Beschränkt TDD wie es sich Kent Beck vorgestellt hat auf „reine Unit Test“. Integrationstest und Coded UI Tests sind da nicht wirklich vorgesehen. Und wenn der Code nicht Testbar ist, können sie dich auf Fehler bei der Software Architektur aufmerksam machen.

„Reine Unit Test“ sind im allgemeinen sehr sinnvoll, da sie im Endeffekt kosten Sparen. Dazu gibt es auch eine Studie die bei IBM und Microsoft durchgeführt wurden (Bin grade zu faul das Raus zu suchen, sollte sich aber über google finden lassen.). Sie sind relative einfach geschrieben und der nutzen ist relative groß.

Integrationstests und Coded UI Test, sind da schon ein anderer Punkt. Sie sind je nach Gegebenheit relative aufwändig. Und der nutzen ist meist nicht mehr so groß wie bei Unit Tests. Teils Testen sie nochmal was die Unit Test schon abdecken. Hier muss man einfach zwischen Kosten und nutzen Abwiegen.

Das „Driven“ in TDD, kann schon mal bei manchen Programmieren für Probleme sorgen. Da es ihnen schwer Fällt, erst den Test zu Schreiben und dann den eigentlichen Code. Meines Erachtens ist es da auch nicht sinnvoll, die Leute dazu zu Zwingen. Sondern es vollkommen OK, wenn sie erst den Code schreiben und dann die Tests. (Wenn es dann Probleme gibt die Test zu schreiben, müssen sie halt dann ein Refactoring machen.)

Ich denke hier sollte man das Eigentliche Ziel nicht aus den Augen verlieren. Man schreibt Tests, weil sie die Produktivität steigern. Und es deutlich günstiger ist, wenn ich den Fehler bei mir finde, als wenn mir ein Kunde von einem Fehler berichtet und ich dann erst auf seinen System den Fehler nachvollziehen muss.

Was mich eigentlich zu einen Anderen Punkt führt (und vielleicht ein anders Thema sind, im Zweifel bitte abtrennen). Nicht nur Unit Test sind ein Schlagwort, sondern Natürlich auch Agile Software Entwicklung mit Scrum oder Kanaban (Halte ich Persönlich für sehr gut und ich denke man sollte schauen, sie soweit wie Möglich Umsätzen). Was dabei meines Erachtens oft übersehen wird. Ist das man mit Menschen zusammen Arbeitet und es im Unternehmen, existierende Strukturen gibt. Und das eigentliche Ziel es nicht, nicht ist Scrum oder sonst was umzusetzen. Sondern Produktiver zu Arbeiten. Was eigentlich auch Grundlage des Agilen Manifests sind.
Scrum usw. können einen dabei Gute Hilfestellung leisten. Ich denke aber was wichtiger ist Individuell auf die Menschen und das Unternehme einzugehen.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

16.827 Beiträge seit 2008
vor 5 Jahren

Palin, evtl nochmals lesenswert...ähnliches Thema wie bei async/await bzw. allgemein in diesem Kontext erwähnenswert.
[Artikel] Unit-Tests: Einführung in das Unit-Testing mit VisualStudio
Viel Erfolg!