Unit Tests in C# schreiben

Hallo Spaß-Coder.

Unit Tests sind eine coole Sache. Warum? Weil ich vor meinen Änderungen am Code leicht prüfen kann, ob alles wie beabsichtigt funktioniert. Dann mache ich meine Änderungen und kann anschließend wieder prüfen, ob immer noch alles funktioniert. Damit sinkt für mich die Gefahr, bei Codeänderungen bestehende Funktion kaputt zu machen.

Es muss nicht zwingend testgetrieben entwickelt werden. Selbst später Unit Tests hinzuzufügen kann hilfreich sein

  • um den Code zu verstehen und
  • um meine Änderung abzusichern (inhaltliche oder strukturelle Änderung).

In verschiedenen Programmiersprachen werden mittlerweile Unit Test Framework bereitgestellt, welche das Schreiben und Ausführen von Unit Test unterstützen. Für .Net sind Microsoft UnitTesting und NUnit bekannte Frameworks. Auf letzteres werden wir hier nicht eingehen, mehr Infos dazu sind unter staratnight.de/blog zu finden.

Machen wir ein kleines Beispiel zu UnitTests mit Microsoft UnitTesting und nutzen dazu diese einfache Klasse mit einer Methode zur Berechnung der ersten n Fibonacci Zahlen nach fn = fn-1 + fn-2 für n > 2 mit f1 = f2 = 1.

Wenn wir diesen Code vorfinden und gerne ändern möchten, ohne dabei die Funktion kaputt zu machen, können wir zunächst Unit Test dafür schreiben. Dafür füge ich der Projekt Solution ein neues Unit Test Projekt hinzu und ergänze dort den Verweis auf das zu testende Projekt Fibonacci. Dies sind dann wie folgt aus:

SolutionExplorerFibonacciWithUnitTest

Für die Namensgebung des Projekts wird einfach ein „.Test“ an den Projektnamen des zu testenden Projekts angehängt. Visual Studio erzeugt eine Testklasse mit der Grundstruktur eines Unit Tests als Vorlage:

Zunächst wird der Klassenname an die zu testende Klasse angepasst: aus UnitTest1 wird damit FibonacciTests. Wichtig sind die beiden Attribute [TestClass] und [TestMethod]. Daran erkennt das Testframework, was bei der Ausführung der Tests zu berücksichtigen ist.

Was wollen wir zuerst prüfen? Beginnen wir mit einfachen Fällen und testen die ersten 2 Zahlen auf Richtigkeit. Für jeden Test schreiben wir eine Testmethode mit dem folgenden Aufbau:

  • Vorbereiten
  • Durchführen
  • Prüfen

Ich beginne jetzt bewusst mit dem Test für n = 2, weil ich weiß, dass n = 1 nicht funktioniert, was wir spätzer ergänzen können.

Der Methodenname sollte sprechend gewählt werden, damit die Absicht des Tests direkt klar wird.

Die Abkürzung sut steht hierbei für subject under test. So kann ich bei vielen Variablen innerhalb des Tests leicht erkennen, was eigentlich zu testen ist. Dies ist die Vorbereitung, eine Instanz der zu testenden Klasse erstellen.

Anschließend wird die zu testende Methode aufgerufen und abschließend das Ergebnis geprüft.

Die Ergebnisprüfung geschieht in Unit Test mit assertions, also mit Behauptungen. Hier behaupten wir, dass die Fibonacci Zahl am Index 0 = 1 ist, ebenso wie die Zahl am Index 1. Die Assert-Klasse von MS UnitTesting stellt einge Behauptungen bereit, z.B. um Boolsche-Ausdrücke oder auf Null zu prüfen.

Lasse ich nun über das Menü von Visual Studio „Test / Run / All Tests“ alle Test laufen, so wird mir das Ergebnis des Tests im Test Explorer angezeigt.

FirstUnitTestResultInTestExplorer

Ab jetzt kann ich nach jeder kleinen Änderung mit der Tastenkombination Strg+R, A alle Tests laufen lassen und erhalte somit ein Feedback, ob meine Änderung die Funktion der bereits geprüften Methoden verändert hat. Grün heißt, alles ok. Bei einem fehlerhaften Test wird dies in rot angezeigt, so dass ich zunächst den Fehler korrigieren kann, bevor ich weitermache.

Erweitern wir die Testmethoden um die Prüfung auf die ersten vier Fibonacci Zahlen.

Das Testergebnis zeigt und einen Fehler – wird ja Zeit, dass hier mal jemand testet!

FailingUnitTestResultInTestExplorer

Als vierte Fibonacci Zahl erwarten wir die 3, tatsächlich gibt die Methode aber 2 zurück. Nun lässt sich gezielt dieser Test debuggen und siehe da – die Methode hat noch nie funktioniert, da sich bei der Berechnung ein Fehler eingeschlichen hat – oh man! Wir korrigieren die Zeile wie folgt und addieren den Vorgänger mit dem Vorvorgänger.

Ein erneuter Aufruf der Tests bestätigt – so ist’s besser.

TwoCorrectUnitTests

Nun könnte ich weitere Unit Tests ergänzen und anschließend den Code der Fibonacci-Klasse umbauen oder ergänzen und kann durch immer wieder ausgeführte Tests prüfen, ob noch alle Ergebnisse wie erwartet berechnet werden.

An dieser Stelle verzichte ich allerdings darauf und zeige noch eine andere Möglichkeit mit Behauptungen – assertions – zu arbeiten. Dazu installiere ich per NuGet die Bibliothek FluentAssertions für mein Testprojekt.

NuGetInstallFluentAssertions

Diese Bibliothek ist als fluent interface aufgebaut, was bedeutet, dass die Methodenaufrufe sehr sprechend formuliert werden. Dabei wird immer von dem zu überprüfenden Objekt ausgegangen, welches ein bestimmtes Verhalten aufweisen sollte. Unser zweiter Unit Test könnte damit so aussehen:

Dieses Beispiel hat leider nicht das Potential die Möglichkeiten und die Lesbarkeit von FluentAssertions in voller Breite zu demonstrieren. Dennoch kann die Behauptung nun als echter Satz gelesen und damit sehr leicht verstanden werden. In unserem Fall:

numbers.Should().BeEquivalentTo(new int[] { 1, 1 , 2, 3});

Also: numbers sollte equivalent sein zu einer Liste mit den Werten 1, 1, 2, 3.

Die Grammatik funktioniert im englischen allerdings besser, weshalb der Code nochmal besser lesbar ist, als diese „Übersetzung“.

Diese Art der Assertions sind selbst dann gut lesbar, wenn ich die Signatur der Assert-Methoden – also welche Parameter was bedeuten – nicht kenne.

Wichtig: das Testergebnis zeigt weiterhin an, dass alles wie erwartet funktioniert 🙂

 

Zusammenfassung

In diesem Artikel haben wir uns grundsätzliche Unit Tests angeschaut und haben hinterfragt, warum diese hilfreich sind. Wir haben das Testframework Microsoft UnitTesting genutzt sowie die Erweiterung für sprechende Behauptungen mit FluentAssertions.

 

Viel Spaß beim Ausprobieren und Happy Coding wünschen

eure Spaß-Coder

 

Verwendete Werkzeuge:

 

Schreibe einen Kommentar

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