Test First reduziert Fehler

Warum sperren sich so viele Entwicklerinnen und Entwickler gegen TDD (Test driven development) oder auch Test First genannt? Meine Erfahrungen zeigen, dass mit Hilfe von TDD viele Softwarefehler erst gar nicht entstehen und somit bares Geld gespart wird.

Ein Beispiel

Letztens sah ich folgendes Beispiel (das Beispiel ist simplifiziert worden, damit der Kern auch herausgearbeitet werden kann):

public class Report {

// ...

/**
* Creates a PDF report from file. If file is null, the result
* should be null as well.
*
* @param file A valid (existing) file or null.
* @return The PDF-Report or null.
*/
public ReportPDF createReport(final File file) {

  // ...
  ReportPDF report = null;

  if (file != null); {
     report = ReportPDF.convertToPDF(filename);
  }

  return report;
}
// ...

}

Dieser Quelltext hat das Code-Review bestanden (warum auch immer). Der Test lief grün. Die Testdatei ist Teil des Projekts, sodass der nightly build auch immer grün lief. Abgesehen davon, dass dieser Test kein Junit Test ist,  sah er (simplifiziert) wie folgt aus:


@Test
public void createReportTest() throws Exception {
   File file = new File("/src/test/resources/testscript.sdc");
   assertTrue(file.exists());
   Report report = new Report();
   ReportPDF pdf = report.createReport(file);
   assertNotNull(pdf);
   ...
   // Weitere Prüfungen, ob das PDF valide ist
}

Der Code lief ein Jahr in Produktion und plötzlich gab es vermehrt Fehler, obwohl der Test immer grün lief. Zunächst: Dieser Test testet nur den „grünen Pfad“ und ist somit kein guter Test.

Meine These: Wäre der Code testgetrieben entwickelt worden, wäre der Fehler sofort aufgefallen bzw. nie entwickelt worden.

Test First lässt einige Fehler gar nicht erst in den Code

Ein Akzeptanzkriterium, welches auch im Kommentar zu finden ist lautet: If file is null, the result should be null as well.

Genau dieser Fall wurde nicht abgetestet, wohl aber im Codereview als gegeben gesehen. Der Report ist null. Nur wenn das File-Object nicht null ist, wird die report variable überhaupt geändert. Sie sollte also null sein, wenn file auch null ist.

Testgetrieben wäre wahrscheinlich sogar als erster Test der folgende formuliert worden:

@Test
public void createReportNullTest() throws Exception {
   Report report = new Report();
   ReportPDF pdf = report.createReport(null);
   assertThat(pdf, nullValue());
}

Die Implementierung wäre einfach:

public ReportPDF createReport(final File file) {
   return null;
}

Das erscheint trivial. Wichtig ist nicht, dass sofort die vollständige Implementierung erfolgt. Wichtig ist, dass nun der Test existiert und immer mitläuft.

Die Implementierung endet, wenn alle Tests grün sind. Der obige Quelltext wäre also niemals implementiert worden. Tatsächlich ist der Fehler tückisch, durchläuft den Compiler. Nebenbei: Findbug hätte ihn gefunden.

Nächste These: Wenn ich testgetrieben implementiere braucht ich Tools wie Findbug nur noch im Integrations-Szenario, weil ich die meisten Bugs gar nicht implementieren kann.

Tatsächlich führt TestFirst nicht nur zu 100% Testcoverage sondern automatisch auch zu guten Tests. Mit dem Tool PI-Test wird der Code mutiert und dabei abgetestet, ob der mutierte Code tatsächlich zu einem rotem Test führt. Erst bei 100% Mutation-Coverage kann also gesagt werden: Die Tests sind nicht nur vollständig sondern testen auch vollständig. Eigene kleine Empirische Untersuchungen mit meinem Kollegen Michael Albrecht haben ergeben: Testgetrieben entwickelter Code hat automatisch auch eine sehr gute Mutation-Coverage.

Fazit

Der „Test First“ Ansatz reduziert nicht nur die Fehler während der Entwicklung (einige Fehler können gar nicht erst gemacht werden) sondern führt auch automatisch zu besseren Code. Die Alternative ist Debuggen. Debuggen führt aber dazu, dass Entwicklerinnen und Entwickler fortwährend Integrationstests händisch ausführen die nicht nachhaltig sind und zudem teuer. (Siehe auch Plädoyer für das Testen – Entwickler verbrennen Geld mit Debuggen).

Facebooktwittergoogle_plusredditpinterestlinkedinmail

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.