Aus dem Kurs: Test Driven Development (TDD) - Grundlagen

FizzBuzz: simples Refactoring

Wir haben uns so langsam warm gelaufen, jetzt möchte ich Ihnen zeigen, wie Sie refaktorisieren können. Dazu betrachten wir unsere Tests. Aktuell ist alles grün, das heißt, wir können jetzt zum Refaktorisieren übergehen, da wir wissen, dass der Code, den wir hier haben, in dem Rahmen funktionstüchtig ist, wie wir das erwarten würden. Als kleine Refaktorisierung entferne ich hier oben erst mal diesen sinnlosen Konstruktor und ich ändere den Namen des Parameters auf "value". Jetzt kompilieren wir einmal und schauen nach, ob wir irgendwas kaputt gemacht haben. Das haben wir nicht, also können wir zum nächsten Test übergehen. Dafür kopiere ich diese Testmethode und, ja, ich könnte das auch etwas einfacher machen, aber dann ist es vielleicht nicht ganz so leicht nachvollziehbar, und ändere den Namen dieser Testmethode in "Given_2_than_2_is_returned", passe die entsprechenden Parameter an und jetzt führe ich meinen Test aus und der müsste auch fehlschlagen, weil ja eine 1 zurückgegeben wird, statt der erwarteten 2. Genau das ist der Fall und damit haben wir erfolgreich die Red-Phase abgeschlossen und können zur Grün-Phase übergehen. Wir wollen unseren Code so langsam weiterentwickeln und ein langsames Weiterentwickeln in kleinen Schritten wäre an dieser Stelle tatsächlich, erst mal nur den Parameter zurückzugeben. Was wir hier schaffen wollen, ist erst einmal eine Testabdeckung, bevor wir zur tatsächlichen Logik übergehen. Führen wir die Tests einmal aus, sie müssten grün werden, sie sind grün, damit sind wir soweit erfolgreich. Ich könnte jetzt noch weiter refaktorisieren, aber das möchte ich an der Stelle tatsächlich nicht. Gehen wir zum dritten Test über. Hier wollen wir bei gegebener 3 ein "Fizz" erhalten, also 3 geben wir rein, "Fizz" sollte rauskommen. Wir testen wieder. Der Test schlägt fehl, weil 3 zurückgegeben wurde, statt "Fizz". Das ist erst mal okay, jetzt können wir unseren Test grün bekommen und das machen wir, indem wir jetzt zunächst erst einmal ein "if(value == 3)" hinschreiben, dann sollte "Fizz" zurückkommen. Was Ihnen jetzt schon auffällt, ist, dass wir ja hier einen Parameter bekommen, den wir unten zurückgeben. Der Parameter ist von int. Was wir nun aber zurückgeben, ist ein String. Ich lasse das an der Stelle, weil ich soll ja nur so viel implementieren, wie die Tests tatsächlich verlangen. Die Tests sind jetzt erfolgreich und sie verlangen eben keine Änderung des Rückgabewertes. Ich kann nun den Rückgabewert aber ändern, und zwar da ich weiß, dass alle Tests erfolgreich sind, indem ich refaktorisiere. Nun stellt sich nur die Frage, was ich refaktorisiere. Sobald ich hier oben ein String draus mache, bekomme ich ein Problem, denn dann werden diese beiden Tests definitiv nicht funktionieren, sie arbeiten ja mit Integern. Es ist an dieser Stelle also schlauer, zunächst die Tests anzupassen. Und das mache ich, indem ich an der Stelle einen String erwarte und den vergleiche mit einem Wert, den ich in einen String wandle. In der Praxis könnte sich das natürlich wesentlich komplexer darstellen, aber uns soll das hier erst einmal helfen, zu zeigen, wie man entweder die Tests oder die Implementierung refaktorisiert, um immer einen gesicherten Zustand zu haben. Jetzt sind alle Tests grün, das Refactoring war also erfolgreich. Und nun kann ich dazu übergehen, hieraus einen String zu machen und mein value ebenfalls in einen String zu wandeln. Wir führen die Tests wieder aus, ob etwas schiefgegangen ist. Das ist es nicht, also kann ich den Teil wieder zurückbauen und habe damit sowohl die Implementierung als auch die Tests, die ich haben wollte, und alles ist weiterhin grün. Ich kann damit also wesentlich sicherer arbeiten, als wenn ich zunächst alles umbaue und dann nicht weiß, an welcher Stelle ich möglicherweise einen Fehler gemacht habe. So kann ich mich darauf konzentrieren, dass ich entweder die Tests anpasse oder die Implementierung und immer genau weiß, warum ein Fehler möglicherweise ausgelöst wurde.

Inhalt