Archiv

Posts Tagged ‘Testing’

Buch-Rezension zu “The Art of Unit Testing (2nd Edition)”

27. November 2013 2 Kommentare

The Art of UnitTesting 2nd editionSeit Ende letzter Woche gibt es mit “The Art of Unit Testing, Second Edition” eine neue Ausgabe meines favorisierten TDD-Buches. Die erste Ausgabe erschien 2009 und beeinflusste mein Verständnis von Test-Driven Development nachhaltig.

Ich war einerseits gespannt auf die neue Ausgabe und andererseits auch ein wenig unentschlossen ob es wirklich eine neue Ausgabe benötigt. Konnte die neue Ausgabe die kleineren Mankos der 1. Ausgabe wirklich beheben? Oder gibt es am Ende ein ganz anderes Buch das nur den gleichen Namen trägt?

 

Aufbau beibehalten, Inhalt überarbeitet

Die Kapitelstruktur der 2. Ausgabe unterscheidet sich nur minimal von der 1. Ausgabe. Der ehemalige Anhang A “Design and testability” ist nun ein “richtiges” Kapitel und Mock-Frameworks werden in einem zusätzlichen Kapitel noch eingehender angeschaut. Die übrigen Kapitel blieben alle erhalten und wer sich nur am Inhaltsverzeichnis orientiert wird diese 2. Ausgabe wohl schnell wieder weglegen.

Die grossen Neuerungen finden sich in den Kapitel selber. Diese wurden komplett überarbeitet und liefern nun eine noch bessere Erklärung wieso man seine Unit Tests auf diese Art organisieren und strukturieren soll. Die Beispiele sind nun so gut dass man dieses Buch auch problemlos einem Anfänger geben kann. Roy Osherove führte zwischen den 2 Büchern zahlreiche TDD-Kurse durch und hat seine dabei gewonnenen Erkenntnisse in die 2. Ausgabe einfliessen lassen. Dadurch wurde auch hinsichtlich der Praxistauglichkeit nochmal ein Schritt nach vorne gemacht, was sich vor allem an den viel klareren Namensregeln zeigt.

Wenn man die erste Ausgabe kennt und täglich mit Unit Tests arbeitet wird man fast nur bei den Mock-Frameworks neue Erkenntnisse bekommen. Die aktuelle Liste lieferte einem eine gute Übersicht und kann einem auch helfen sich gegen ein bestimmtes Framework zu entscheiden:

MSFakes might be free and included with Visual Studio, but it will cost you a lot of money down the line in developer hours, fixing and trying to understand your tests.

Wer seine erste Ausgabe nicht nur als Staubfänger nutzt sollte sich die 2. Ausgabe zulegen. Ob man das Buch als Nachschlagewerk, zum Auffrischen seiner Unit Testing Kenntnisse oder für neue Teammitglieder braucht – die überarbeiteten Kapitel sind einfacher zu verstehen und näher an der Praxis.

 

Fazit

Mit der 2. Ausgabe konnte Roy Osherove die hohen Erwartungen übertreffen und ein noch besseres Buch über Unit Testing liefern. Die darin beschriebenen Vorgehensweisen helfen in der Praxis die Unit Tests im Griff zu behalten und eine Struktur hinein zu bringen. Wenn man die erste Ausgabe besitzt und damit noch arbeitet lohnt sich auch der Kauf der 2. Ausgabe.

 

Zum Buch

The Art of Unit Testing, Second Edition” von Roy Osherove, November 2013 Manning, ISBN 978-1-6172-9089-3, 296 Seiten, Englisch

 

Schlagworte: , , ,

Die Testpyramide

10. März 2013 2 Kommentare

Seit einigen Monaten stosse ich immer wieder auf das Konzept der Testpyramide. Ich finde dieses Bild sehr passen, da es die wesentlichen Aspekte auf den Punkt bringt. Um ein System wirklich zu testen gilt es mehrere Ebenen anzuschauen. Die Testpyramide zeigt diese auf und vermittelt auf eine leicht verständliche Weise wie sich die Anzahl der Testfälle staffeln soll:

Die Testpyramide

 

Unit-Tests als Grundlage

Unit-Tests bilden die Basis der Testpyramide. Die kleinstmöglichen Tests sollten sicherstellen dass das System im Kern funktioniert. Eine wichtige Eigenschaft von Unit-Tests: Sie sind Schnell. In wenigen Sekunden sollte man wissen ob es überhaupt Sinn macht die länger laufenden Tests zu starten. Diese Sekunden sind wohlgemerkt nicht für einen einzigen Tests gedacht, sondern für alle Unit-Tests zusammen – womit ein Unit-Test der länger als 1/100 Sekunde dauert schon als langsam gelten muss.

Damit Tests so schnell sind dürfen sie nur wenig testen. Weder eine Verbindung zur Datenbank noch ein Zugriff aufs Dateisystem oder Aufruf eines Webservices ist erlaubt. All diese Abhängigkeiten müssen entfernt werden. Ob dies mittels Konfiguration oder mit Mocks gemacht wird spielt dabei keine Rolle.

Die Geschwindigkeit alleine kann aber nicht das einzige Kriterium für einen Unit-Test sein. Sonst besteht die Testsuite am Ende nur aus leeren Methoden. Das was man testet soll auch noch Sinn machen. Und einem in die richtige Richtung weisen wenn einmal ein Test fehlschlägt. So ist man schnell einmal bei mehreren Kriterien die von Ben Rady und Rod Coffin in “Continuous Testing” mit dieser Abkürzung zusammengefasst werden:

FIRE: Fast, Informative, Reliable and Exhaustive

 

Integrationstests

Zu wissen dass der eigene Code für sich alleine funktioniert ist ein Anfang. Damit weiss man aber noch nicht ob der Code auch mit anderen Teilen funktioniert. Hier kommen die Integrationstests ins Spiel.

Auf dieser Ebene werden all die Abhängigkeiten angeschaut die man bei den Unit-Tests entfernt hat. Was zuerst nach vermeidbarem Zusatzaufwand aussieht hat sehr wohl seine Berechtigung. Es genügt wenn man das Erzeugen, Speichern, Aktualisieren und Löschen eines Objekts in der Datenbank ein Mal pro Klasse testet. Dies hat die gleiche Aussagekraft (ist aber deutlich schneller) wie wenn man in allen Unit-Tests immer mit den Objekten aus der Datenbank arbeiten würde.

Da weniger Tests mit den Umsystemen nötig sind wirkt sich deren Ausführungsdauer nicht so stark auf die Länge des gesamten Testlaufs aus.

 

Akzeptanztests

Die Akzeptanztests bilden die Spitze der Testpyramide. Hier gilt es die Anwendung aus Sicht des Benutzers zu testen. Vom GUI durch die Geschäftslogik hin zur Datenbank und den externen Webservices soll hier alles geprüft werden.

Da man bereits weis das sowohl der Kern der Anwendung funktioniert und der auch mit den Umsystemen korrekt zusammenarbeitet benötigt man nur noch wenige Akzeptanztests. Diese dürfen noch einmal langsamer sein als die Integrationstests und sollen als letzte Stufe die Korrektheit der gesamten Anwendung belegen.

Und da es so wenige Tests sind kann man diese auch mit dem Kunden/Endbenutzer besprechen. Müssen wirklich nur die wichtigsten Tests angeschaut werden hat man gute Chancen dass dies auch wirklich gemacht wird.

 

Reihenfolge & Einschränkungen

Die Testpyramide gibt keine Reihenfolge für die Erstellung der Testfälle vor. Wenn es bei der Ausführung auch am meisten Sinn macht mit den Unit Tests zu beginnen so ist man beim Erstellen frei.

Hat man Glück und der Kunde will an Akzeptanztests mitarbeiten kann man einen Top-Down Ansatz wählen. Man beginnt mit einem fehlgeschlagenen Akzeptanztest und schreibt so lange Integrations- und Unit-Tests bis dieser erfüllt wird. Alternativ kann man aber auch mit den Unit-Tests beginnen und sich nach oben arbeiten.

Die Testpyramide ist aber nicht perfekt. Es gibt etliche Testarten die darin keinen Platz finden. Wo platziert man beispielsweise die Explorationstests? Oder die Performancetests? Trotz dieser Einschränkungen finde ich das Bild der Testpyramide sehr gelungen.

Schlagworte: ,

TDD: Denken erlaubt

27. Februar 2013 2 Kommentare

Test-Driven Development (TDD) gibt auch heutzutage noch viel zu diskutieren. Was mir dabei immer wieder auffällt: Es scheint als ob vor lauter Red-Green-Refactor vergessen geht das man eigentlich Software entwickeln soll. Sobald man sich mit Tests beschäftigt vergisst man das grosse Ganze. Oder wieso schreiben gestandene Software-Entwickler einen Test nach dem anderen der ihnen nur bestätigt das 1 + 1 wirklich 2 ergibt?

 

Code Katas als Ursache?

Code Katas sollen einem dabei helfen sich bei einer Übung auf einen bestimmten Aspekt zu konzentrieren. Dieser Aspekt ist oft TDD, doch gibt es genügend andere Aspekte die man in den Vordergrund stellen kann (wie die Bedienung der IDE nur mit der Tastatur oder Patterns wie das Single Responsibility Principle).

Will man damit TDD erproben nimmt man in der Regel ein ganz einfaches Beispiel. Man will sich ja nicht lange mit dem Problem beschäftigen sondern TDD lernen. Entsprechend schreibt man viele Tests und denkt wenig über die Lösung nach – diese ist ja durch einfache Beispiele wie FizzBuzz gewollt. Man trainiert also ständig mit einfachen Beispielen und vielen Tests. Die Videos zu diesen Trainings zeigen entsprechend viele Tests und wenig Gedanken über die Lösung.

Kurzum: Der Feedback-Loop für den Entwickler zeigt ganz klar dass er TDD erst richtig macht wenn wer viel testet und wenig denkt. Das Problem ist nur das dies komplett falsch ist.

 

Software-Entwicklung vor TDD

Gehen wir einen Schritt zurück. Bevor man mit TDD Software entwickeln wollte machte man in der Regel diese Schritte:

  1. Problem analysieren
  2. Lösung erarbeiten
  3. Programmieren
  4. Testen
  5. Veröffentlichen
  6. Bugs fixen
  7. Veröffentlichen

Bei der iterativen Software-Entwicklung folgt man ebenfalls dieser Schrittfolge – einzig der Umfang behandelt nicht mehr das ganze System sondern nur einen Teil.

Die Idee von TDD war es ursprünglich einmal die Schritte 3 & 4 zu kombinieren. So hoffte man die teure Behebung von Bugs nach der Veröffentlichung zu minimieren. Nie war es das Ziel die Phase der Problemanalyse oder dem Erarbeiten der Lösung zu streichen.

 

Babysteps ins Chaos

Die Analyse der Problemstellung und das erarbeiten der Lösung ging irgendwo bei der Einarbeitung in TDD verloren. Irgendwann sollten Babysteps (wie 1+1 = 2) die Verständnislücken der Problemstellung füllen und die Lösung herausfallen. Nur wo passiert dies? Viel eher stehen all die minimalen Tests nur im Weg und erschweren die Weiterentwicklung der Software. Alleine mit Babysteps lösen wir das Problem nicht.

 

Schrittgrösse variieren

Wie aber geht man “richtig” vor? Kent Beck zeigt in “TDD By Example” wie man erst das Problem analysieren soll, eine Liste mit Testfällen (und noch anzugehenden Problemen) führt und vor allem wie man die Schrittgrösse variieren kann.

Das Red-Green-Refactor soll man beibehalten. Aber wenn man weiss wo hin man will und welche Lösung man verfolgt kann man grössere Schritte machen. Wie gross diese Schritte sind hängt vom konkreten Problem, der Vertrautheit mit der Technologie und dem angepeilten Lösungsweg ab.

Für den Lösungsweg greife ich oft auf Flussdiagramme zurück. So altmodisch die auch sein mögen, für eine grobe Skizzierung der Abläufe finde ich diese Art der Darstellung sehr hilfreich. Auch in der objektorientierten Programmierung gibt es unzählige Teilaufgaben die mit einem Flussdiagramm ideal abgebildet werden können.

Mit wenigen Worten pro Tätigkeit kann ich die einzelnen Kernaufgaben herausarbeiten. Ich sehe so auf einen Blick was alles zur Lösung dazu gehört und was es für Abhängigkeiten gibt. Passende Testfälle (und damit die Schrittgrösse) lassen sich so ebenfalls finden. Beim Programmieren tauchen dann immer noch genügend neue Testfälle auf. Diese können aber auch erst einmal nur auf eine Liste abgelegt und erst später priorisiert (oder gelöscht) werden.

 

Beispiele

Für das was ich bisher beschrieben habe gibt es zahlreiche gute Beispiele. Besonders empfehlen kann ich den Pluralsight-Kurs “Outside-In Test-Driven Development” von Mark Seemann. Darin wird erklärt wie man mit TDD bei den Akzeptanztests beginnt und schrittweise verfeinert bis der notwendige Code geschrieben ist. Beginnt man mit den Akzeptanztests verliert man das eigentliche Ziel nicht aus den Augen. Das Mantra Red-Green-Refactor bleibt gültig ohne dass man sich in den Babysteps verliert. Und sind diese kleinen Schritte doch nötig hat man ein Rahmenwerk das einem daran erinnert wann genug ist.

Wer Bücher bevorzugt findet in “Rails 4 in Action” von Ryan Bigg eine ausführliche Anleitung um testgetrieben eine Webanwendung zu erstellen. Hier wird noch mehr Wert auf die praktische Implementierung von TDD gelegt und gezeigt wie man mit unterschiedlichen Testebenen zu einer Lösung kommt.

 

Fazit

Nur weil man TDD macht muss man noch lange nicht aufhören selber zu denken. Eine Analyse der Lösung und ein grober Lösungsweg braucht es noch immer bevor man sich dem programmieren zuwendet. Verzichtet man auf diese grundlegenden Dinge schiebt man nur Code herum. Dies ist nicht nur ineffizient sondern auch teuer – sowohl beim erstmaligen schreiben wie auch bei all den kommenden Änderungen.

Daher unbedingt erst einmal abklären was man eigentlich machen will. Ist das “Was” klar geht es ans “Wie”. Ob man dazu ein Flussdiagramm oder eine sonstige Skizze macht spielt keine Rolle – wichtig ist das man sich überlegt wie man vorgehen will. TDD und Red-Green-Refactor können viel besser, klarer und sinnvoller angewendet werden wenn man diese Vorarbeiten macht.

Schlagworte: ,

Moq für komplexere Anwendungsfälle

29. August 2012 Kommentare aus

Moq ist eine kleine Mock-Bibliothek, die ich hier bereits einmal vorgestellt habe. Für die meisten Anwendungsfälle genügt es die Rückgabewerte von Methoden zu beeinflussen. Ab und zu wäre es aber praktisch ein wenig mehr mit den Mocks machen zu können. Heute zeige ich wie Moq einem bei spezielleren Anwendungsfällen unterstützen kann.

 

Flexible Rückgabewerte

Mit Moq ist es kein Problem verschiedene Rückgabewerte zu liefern – so lange sich die Parameter beim Methodenaufruf unterscheiden. Der notwendige Code um den Wert „A“ für die Id 1 und „B“ für die Id 2 zu erhalten sieht so aus:

public interface ISimpleDemo
{
    string GetValue(int id);
}

[TestMethod]
public void MultipleReturnsForDifferentInputs()
{
    var mock = new Mock<ISimpleDemo>();
    mock.Setup(service => service.GetValue(1)).Returns("A");
    mock.Setup(service => service.GetValue(2)).Returns("B");

    Assert.AreEqual("A", mock.Object.GetValue(1));
    Assert.AreEqual("B", mock.Object.GetValue(2));
}

Hin und wieder steht man aber vor der Aufgabe bei einer Methode ohne Parameter verschiedene Rückgabewerte definieren zu müssen. Der direkte Versuch die gewünschten Werte nach einander zu definieren schlägt leider fehl:

public interface IBlogDemo
{
    string GetNextName();
}

[TestMethod]
public void MultipleReturnsTheWrongWay()
{
    var mock = new Mock<IBlogDemo>();
    mock.Setup(service => service.GetNextName()).Returns("A");
    mock.Setup(service => service.GetNextName()).Returns("B");
    mock.Setup(service => service.GetNextName()).Returns("C");

    Assert.AreEqual("A", mock.Object.GetNextName()); // Gibt "C"
    Assert.AreEqual("B", mock.Object.GetNextName());
    Assert.AreEqual("C", mock.Object.GetNextName());
}

So überschreibt man nur den Rückgabewert und der Tests endet mit dieser Fehlermeldung:

Assert.AreEqual failed. Expected:<A>. Actual: <C>.

Damit man diesen Test erfüllen kann benötigt es einen anderen Ansatz. Mit Hilfe einer Queue kann man die Werte in der gewünschten Reihenfolge ablegen (auch null-Werte sind so möglich). Die Returns-Funktion von Moq ruft nun die Dequeue-Methode auf und bekommt die für den Test benötigten Werte zurück:

[TestMethod]
public void MultipleReturnsRight()
{
    var mock = new Mock<IBlogDemo>();
    var results = new List<string> {"A", "B", "C", null};
    var pq = new Queue<string>(results);

    mock.Setup(c => c.GetNextName()).Returns(pq.Dequeue);

    Assert.AreEqual("A", mock.Object.GetNextName());
    Assert.AreEqual("B", mock.Object.GetNextName());
    Assert.AreEqual("C", mock.Object.GetNextName());
    Assert.IsNull(mock.Object.GetNextName());
    // ==> Funktioniert
}

 

Wurde eine Methode aufgerufen?

Moq bietet einem die Möglichkeit einzelne Aspekte des verwendeten Mocks zu überprüfen. Muss man wissen wie oft eine Methode des Mocks aufgerufen wurde ist die Funktion Verify sehr hilfreich. Der notwendige Code um zu prüfen ob GetValue mit Parameter 1 genau einmal aufgerufen wurde wird so sehr einfach:

[TestMethod]
public void ExactlyOnceCalled()
{
    var mock = new Mock<ISimpleDemo>();

    mock.Object.GetValue(1);

    mock.Verify(db => db.GetValue(1), Times.Once());
}

Wird die Methode nicht mit dem erwarteten Parameter aufgerufen (zum Beispiel in dem man im oberen Code-Ausschnitt GetValue mit dem Wert 2 aufruft), wirft Moq eine entsprechende Fehlermeldung:

Test method TestProject1.UnitTest1.ExactlyOnceCalled threw exception:
Moq.MockException:
Expected invocation on the mock once, but was 0 times: db => db.GetValue(1)
No setups configured.
Performed invocations:
ISimpleDemo.GetValue(2)

Es kann vorkommen das man nicht genau weiss mit welchem Parameter die Methode aufgerufen wurde (wenn beispielsweise in der zu testenden Klasse neue Instanzen erzeugt werden). In diesem Fall kann man die It-Klasse verwenden:

[TestMethod]
public void ExactlyOnceCalledUnknownParameter()
{
    var mock = new Mock<ISimpleDemo>();

    mock.Object.GetValue(1);

    mock.Verify(db => db.GetValue(It.IsAny<int>()), Times.Once());
}

Auch bei der Anzahl der Aufrufe ist Moq sehr flexibel. Ob eine Methode einmal, nie, genau x mal oder innerhalb eines Bereiches aufgerufen wurde kann man alles mit der Times-Klasse überprüfen lassen:

[TestMethod]
public void TimesHasManyUsages()
{
    var mock = new Mock<ISimpleDemo>();

    mock.Object.GetValue(1);
    mock.Object.GetValue(2);
    mock.Object.GetValue(2);

    mock.Verify(db => db.GetValue(1), Times.Once());
    mock.Verify(db => db.GetValue(2), Times.AtLeastOnce());
    mock.Verify(db => db.GetValue(2), Times.Between(1, 2, Range.Inclusive));
    mock.Verify(db => db.GetValue(2), Times.Between(1, 3, Range.Exclusive));
    mock.Verify(db => db.GetValue(2), Times.Exactly(2));
    mock.Verify(db => db.GetValue(3), Times.Never());
}

 

Fazit

Moq unterstützt einem auch bei komplexeren Einsatzszenarien. Die Möglichkeiten von Verify sind vielfältig und können einem dabei helfen so manches Testszenario abzudecken. Dabei sind It.IsAny und Times eine grosse Hilfe.

Allerdings darf man bei so viel Flexibilität das Ziel des Tests nicht aus den Augen verlieren. Sobald man sich auf zu viele Implementationsdetails einlässt werden die Tests sehr schnell unwartbar. Im Zweifel sollte man daher lieber erst versuchen mit einer besseren Code-Struktur den Test einfacher zu gestalten.

Schlagworte: ,

Buch-Rezension zu “Professional Test Driven Development with C#”

30. Januar 2012 Kommentare aus

Professional Test Driven Development with C#” von Jeff McWherter und James Bender erschien im Mai 2011 bei Wiley. Das Buch richtet sich an all die Entwickler, die ihre C#-Anwendungen nach Test-First entwickeln wollen.

Wer sich bisher noch nicht mit dem Thema Test Driven Development (TDD) beschäftigt hat findet in den ersten beiden Teilen des Buches eine gute und ausführliche Einführung ins Thema.

 

 

TDD Szenarien aus der Praxis

Der dritte Teil widmet sich den praxisorientierten TDD Szenarien. Im Gegensatz zu vielen anderen Büchern geht es hier um die schwerer zu testenden Teile:

  • ASP.Net WebForms
  • ASP.Net MVC
  • JavaScript
  • WPF
  • Silverlight
  • WCF

Im Buch wird zu jeder dieser Technologien aufgezeigt wo die besonderen Herausforderungen liegen und wie man diese Testen kann. Wann immer möglich sollte man die dazu passenden Patterns verwenden. Geht dies nicht ist man auf sich alleine gestellt: Das Buch erklärt leider nur genau einen Weg um die entsprechende Technologie zu testen.

 

Werkzeuge und Katas

Als Abschluss gibt es einen Teil der sich den Werkzeugen widmet. Bei der Vielzahl möglicher Test-, Mock- und DI-Frameworks ist es nicht leicht eine Auswahl zu treffen. Die Autoren vergleichen jeweils einige Werkzeuge aus dem gleichen Bereich und nennen die für sie wichtigsten Unterschiede. Auch wenn dies die eigene Recherche nicht ersetzt, so ist dies doch ein guter Ausgangspunkt um seinen Werkzeugkasten zusammen zu stellen.

Im Appendix wir noch auf das Thema (TDD-) Katas eingegangen. Kleinen Übungen sollen einem dabei helfen das gelernte zu verinnerlichen. Die Idee stammt aus dem Kampfsport und hilft dort die richtigen Aktionen und Bewegungen zur richtigen Zeit zu machen. Übertragen auf TDD bedeutet dies: Hat man immer und immer wieder geübt wie man erst einen Test und dann den produktiven Code schreibt, geht dies in einem über und man wendet die gleiche Technik auch mit dem Code direkt vor einem an.

 

Ein E-Book aber kein PDF

Wiley bietet für dieses Buch eine E-Book Version an, aber leider nur in den Formaten ePub und mobi. Gerade wenn ein Buch viele Code-Beispiele hat habe ich neben der Kindle-Version sehr gerne noch ein PDF. Nicht nur sieht der Code dort meist besser aus, er lässt sich auch einfach kopieren. Mir schein als ob Wiley der Konkurrenz in diesem Bereich noch weit hinterher hinkt.

 

Fazit

Den beiden Autoren ist in einem oft behandelten Themenbereich ein gutes Werk gelungen. Die beiden Einführungsteile liefern alles nötige Grundwissen damit man auch als Anfänger durchstarten kann. Die Auswahl eines Werkzeugkastens und das vermitteln der Ideen rund um TDD runden dieses Buch ab. Mir fehlen hier einzig noch einige alternative Ansätze zum Testen der behandelten .Net-Komponenten.

The Art of Unit Testing“ ist zwar immer noch mein Favorit zum Thema TDD, dieses Buch folgt aber mit einem sehr kleinen Abstand.

 

Zum Buch

Professional Test Driven Development with C#” von Jeff McWherter und James Bender, 2011 Wiley, ISBN: 978-0-470-64320-4, 360 Seiten, Englisch

Schlagworte: , , ,

Leichter testen mit Moq

30. Dezember 2011 1 Kommentar

Moq ist ein kleines und sehr einfaches Mock-Framework für .Net / C#. Obwohl es solche Frameworks wie Sand am Meer gibt, so sticht Moq doch heraus. Allzu oft ist das Aufsetzen eines Mocks sehr umständlich und der Test Code wird unnötig kompliziert. Moq dagegen nutzt eine sehr klare Syntax und verfügt über ein praxisorientiertes Standardverhalten.

 

Wozu Mocks?

Bei Unit-Tests möchte man möglichst nur eine kleine Einheit testen. Meistens aber gibt es Abhängigkeiten zu anderen Einheiten die man für diesen Test ignorieren möchte. Ist man am Erstellen eines Rabattsystems für einen Warenkorb möchte man nicht jedes Mal überprüfen ob der Warenkorb korrekt aus der Datenbank geladen werden kann.

Es kann auch sein das die Abhängigkeiten für die zu testende Einheit noch gar nicht existieren. Soll ein Client und ein Service parallel entwickelt werden einigt man sich zuerst auf eine Schnittstelle. Bis der Service läuft muss man mit dem Client gegen etwas testen können. Mocks können dieses „etwas“ sein und den Service simulieren.

Was zwischen 2 Systemen geht funktioniert genauso auch mit gewöhnlichen Klassen. Hier kann man ein Interface definieren und erzeugt daraus einen Mock bis man eine richtige Implementierung hat.

 

Moq verwenden

Moq installiert man am besten über NuGet. Man bekommt so die aktuelle Version und kann später sehr einfach das installierte Paket aktualisieren. Wer dies nicht will findet den Code auf der Projektseite. Die Referenz auf Moq braucht es nur im Testprojekt, das Projekt mit dem produktiven Code muss von Moq nichts wissen.

Bevor man mit Moq loslegt sollte man einen Blick auf den QuickStart Artikel im Wiki werfen. Dort werden die häufigsten Anwendungsfälle mit Codebeispielen gezeigt.

Für mein Beispiel nutze ich das Interface IPersonService und die Klasse Person:

public interface IPersonService
{
    Person GetPersonById(int id);
}

public class Person
{
    public string LastName { get; set; }
    public string FirstName { get; set; }
}

Ich möchte erreichen dass immer wenn ich die Methode GetPersonById(1) aufrufe eine bestimmte Instanz von Person zurückgegeben wird. Dazu genügt es die Person zu instantzieren und 2 Zeilen Code um Moq aufzusetzen:

[TestMethod]
public void ShowHowToMockAnInterface()
{
    Person person = new Person(){LastName = "Graber", FirstName = "Johnny"};
            
    var mock = new Mock<IPersonService>();
    mock.Setup(service => service.GetPersonById(1)).Returns(person);

    Assert.AreEqual(person, mock.Object.GetPersonById(1));
}

Wer sich bisher nicht mit Lambda-Ausdrücken beschäftigt hat wird vielleicht ein wenig verwirrt sein. Ich finde den Code so aber sehr leserlich und verständlich. Will man später seine Methode umbenennen wird mit den Tools von Visual Studio dieser Aufruf gefunden. Würde man statt dem Lambda einen String benutzen ist dies nicht immer der Fall.

Um eine realistischere Verwendung von Moq zu zeigen soll nun die Klasse Worker die Person holen. Diese kennt nur das Interface und weiss nichts von Moq:

public class Worker
{
    private readonly IPersonService _personService;

    public Worker(IPersonService personService)
    {
        _personService = personService;
    }

    public Person GetPerson()
    {
        return _personService.GetPersonById(1);
    }
}

Der dazugehörige Test sieht fast gleich aus:

[TestMethod]
public void ShowUsageOfMock()
{
    Person person = new Person() { LastName = "Graber", FirstName = "Johnny" };

    var mock = new Mock<IPersonService>();
    mock.Setup(service => service.GetPersonById(1)).Returns(person);   

    Worker worker = new Worker(mock.Object);

    Assert.AreEqual(person, worker.GetPerson());
}

 

Stabile Tests

Was mir an Moq besonders gefällt ist das Standardverhalten. Wird eine Methode des Interfaces aufgerufen die man nicht gemockt hat liefert Moq den Standardwert zurück. Wenn ich eine neue Methode im Interface hinzufüge und diese im Worker aufrufe funktionieren meine bisherigen Tests auch weiterhin.


public interface IPersonService
{
    Person GetPersonById(int id);

    bool IsActive(Person person); // Neue Methode
}

public class Worker
{
    ...
    public Person GetPerson()
    {
        Person p = _personService.GetPersonById(1);
        _personService.IsActive(p); // neuer Aufruf
        return p;
    }
}

Durch dieses Verhalten sind die Tests weniger Fehleranfällig. Wenn ich aber einen Fehler will sobald andere Methoden von meinem Mock aufgerufen werden kann ich dies im Konstruktor aktivieren:

var mock = new Mock<IPersonService>(MockBehavior.Strict);

 

Wo Moq an seine Grenzen stösst

Wie viele Mock-Frameworks kann auch Moq keine statischen Klassen und Methoden mocken. Arbeitet man an einem neuen Projekt kann man diese Einschränkung umgehen in dem man möglichst auf statischen Methoden verzichtet und wo nicht anders möglich diese in einen Wrapper packt.

Will man aber in einem grossen bestehenden Projekt mit vielen statischen Methoden mit Unit Tests beginnen ist die Einschränkung von Moq ein Problem. In dem Fall sollte man sich TypeMock und JustMock anschauen. Diese Frameworks umgehen die Einschränkungen mit statischen Klassen und Methoden indem sie sich sehr tief im .Net Framework einklinken und Methodenaufrufe umleiten.

 

Fazit

Moq ist ein sehr hilfreiches Werkzeug für Unit Tests. Es macht genau was man von ihm erwartet und braucht dazu nur eine minimale Konfiguration. Dadurch kann man sich auf das konzentrieren was man wirklich machen will: Mit Unit-Tests neue Funktionalität einbauen.

 

Schlagworte: , ,

Buch-Rezension zu “Continuous Testing”

17. Oktober 2011 5 Kommentare

Continuous Testing – with Ruby, Rails, and JavaScript” von Ben Rady und Rod Coffin erschien im Juni 2011 bei The Pragmatic Programmers. Continuous Testing geht noch einen Schritt weiter als Test Driven Development und versucht die Feedback-Schlaufe von Red-Green-Refactor noch mehr zu verkürzen.

[Hinweis: Ich habe dieses Buch über die .Net User Group Bern erhalten, die am User Group Programm von O’Reilly teilnimmt. Wie immer wenn ich über Bücher blogge schreibe ich was mir daran gefällt und was nicht. Dies mache ich unabhängig davon ob ich ein Rezensionsexemplar bekomme oder das Buch selber kaufe.]

 

Continuous Testing?

Bei Continuous Testing geht es um die unmittelbare und automatische Rückmeldung ob die gerade getroffene Entscheidung die Software in die richtige Richtung bringt. Rady und Coffin nutzen das Speichern einer Datei als Taktgeber. Das Speichern (oder genauer die dadurch veränderte Datei) startet im Hintergrund und ohne weiteres Zutun des Entwicklers die automatisierten Tests. Als Entwickler braucht man nur noch die Konsole anzuschauen und sieht anhand der Farben sofort ob die Tests noch funktionieren.

Kontinuierliches Testen kann man wie von den Autoren gleich zu Beginn des Buches erwähnt recht einfach haben:

Open your favorite editor or IDE. Take whatever key you have bound to Save and rebind it to Save and Run All Tests. Congratulations, you’re now testing continuously.

Wer selber schon automatisierte Tests geschrieben hat weiss dass dies damit nicht wirklich gemacht ist: Tests brauchen eine gewisse Zeit um ein Ergebnis zu liefern, es gibt bald einmal Abhängigkeiten mit Umsystemen und wenn man noch GUI-Tests benötigt wird es erst recht aufwendig.

Dies ist auch den Autoren bekannt. Daher steht diese Textstelle auch am Anfang und nicht am Ende des Buches. Es ist nicht das Ziel von Continuous Testing immer alle Tests laufen zu lassen – das ist die Aufgabe von Continuous Integration nach dem commit. Es gibt viele Tests die in sehr kurzer Zeit einem eine grosse Gewissheit geben, dass alles läuft wie es soll. Die Tests gilt es zu finden und häufig auszuführen.

Um einem in die Lage zu bringen diese Tests zu identifizieren und mit einem passenden Ansatz zu testen ist das Buch in 3 Teile gegliedert.

 

Teil 1: Ruby und Autotest

Der erste Teil geht das Konzept Continuous Testing für Ruby an. Autotest wird dabei verwendet um die Dateien zu überwachen und die Tests auszuführen. Mit einfachen Erweiterungen kann man Autotest dazu bringen auch gleich noch die einzelnen Dateien nach TODOs abzusuchen.

Neben den Werkzeugen wird in diesem Teil auch grundlegendes zum Testen vermittelt (wie FIRE: Fast, Informative, Reliable and Exhaustive). Dies bleibt aber oberflächlich und ist vor allem da um das Wissen aufzufrischen (im Sinne von da war doch mal was).

Hilfreicher fand ich wie diese Theorie dann konkret angewendet werden kann. Als gelungenes Beispiel fand ich das Mocken von Twitter oder das Erkunden der API von MongoDB.

 

Teil 2: Rails, JavaScript und Watchr

Im 2. Teil geht es um komplexere Projekte. Bei Rails sind migrations (anpassen der Datenbank) ein Thema das man eingehend testen will. Aber auch JavaScript sollte bereits zur Entwicklungszeit getestet werden. Die so hinzukommenden Abhängigkeiten (Datenbank, Webbrowser, usw.) müssen mit anderen Werkzeugen getestet werden als die einfachen Ruby-Projekte aus dem 1. Teil.

Um die Dateien zu überwachen wird hierfür Watchr vorgestellt. Dieses Tool kann im Gegensatz zu Autotest nicht nur Tests ausführen, sondern jedes beliebige Kommando (wie Beispielsweise die Prüfung von CSS-Dateien mit Sass).

Da mit zunehmender Komplexität die Tests länger laufen muss auch in diesem Bereich etwas gemacht werden. Spork hilft hier indem die Testumgebung nicht bei jedem Testdurchlauf neu initialisiert werden muss.

Das Testen von JavaScript wird auf mehreren Ebenen angegangen. Einfache Dinge die keinen DOM benötigen werden mit Node.js getestet, für die komplexeren Szenarien kommt jsdom zum Einsatz. Für mich hätte man die JavaScript-Tests gerne eingehender behandeln können. So blieben bei mir doch mehr Fragen offen als ich Antworten erhalten habe. Dafür hätte man das Kapitel über funktionales JavaScript im Appendix (Teil 3 des Buches) gerne weglassen können.

 

Ruby-Kenntnisse nötig

Im Gegensatz zu den Autoren bin ich der Meinung dass man die Grundlagen von Ruby kennen muss um dem Buch folgen zu können. Man muss sicher kein Experte sein. Wenn man aber zu einem neuen Konzept auch noch gleich eine neue Sprache lernen soll wird die Lernkurve sehr steil.

Continuous Testing ist ein sprachunabhängiges Konzept und lässt sich somit auch ausserhalb von Ruby und JavaScript nutzten. Um die Arbeitsumgebung aufzuzeigen werden aber sehr viele Werkzeuge eingeführt die spezifisch für Ruby und Rails sind. Ob sich dies wirklich jemand durchlesen will der sich nicht für Ruby interessiert? Ich würde sagen Nein.
Lässt man diese Teile aber weg bleibt vom Buch nicht mehr viel übrig.

 

Fazit

Continuous Testing ist ein interessantes Konzept. Die automatische Testausführung scheint zwar nur ein kleiner Schritt zu sein. Ein klein wenig Code zu schreiben und gleich sehen zu können ob die Tests damit erfüllt sind hat etwas sehr motivierendes. Es bedingt aber das man seine Software testgetrieben entwickeln will.

Will man so Software in Ruby/Rails entwickeln ist dieses Buch ein guter Einstieg. Macht man schon TDD und will von Ruby nichts wissen schaut man sich besser nach einem anderen Buch um.

 

Zum Buch

Continuous Testing – with Ruby, Rails, and JavaScript” von Ben Rady und Rod Coffin, 2011 The Pragmatic Programmers, ISBN 978-1-93435-670-8, 160 Seiten, Englisch

Schlagworte: , ,
Folgen

Erhalte jeden neuen Beitrag in deinen Posteingang.

Schließe dich 297 Followern an