HowTo: UnitTests und Einführung in Mocking mit Rhino.Mocks

In dem letzten UnitTest HowTo haben wir den ersten Schritt in Richtung “bessere Software” getan.

Was bei Unit Tests beachtet werden sollte: Ein Unit Test soll genau eine Sache testen – abgeschottet von allen anderen Sachen.
Phil Haack hat einen sehr guten Blogpost darüber geschrieben (und noch einen anderen ;) ) – der auch mich etwas “wachgerüttelt” hat.

image

Was bedeutet das:
Wenn wir angenommen eine 3-Schichten Architektur haben und unseren Service Layer testen möchten, sollten wir tunlichst vermeiden über den Data Access Layer zu gehen und so direkt zur DB zu reden. Die DB kann sich verändern!

Warum?
Wenn man direkt mit der DB redet, muss man sich auf bestimmte Daten verlassen – d.h. statische Daten laden in den Tests. Falls die Daten in der DB mal verloren gehen, gehen leider die UnitTests nicht mehr (selber in einem Projekt soeben bemerkt ;) ).

Ich mach eine Testdatenbank!
Man kann natürlich eine direkte Testdatenbank habe – allerdings ist es dann trotzdem recht schwierig eine “valides” Umfeld bei jedem einzelnen Test zu haben. Oder man resettet nach jedem Test die TestDB – allerdings laufen dann die Unit-Tests sehr zäh ab und es ist auch recht schwierig zu managen.

Mein Idealbild:
Bei jedem Unit Test möchte ich meine Umgebung exakt auf den Testfall konstruieren – sodass ich genau das Verhalten testen und daraufhin implementieren kann.

Das Zauberwort: Mocking
Durch den Einsatz von Schnittstellen usw. kann man natürlich überall Fake / Dummy Klassen erstellen – wie z.B. ich es hier getan hab mit den statischen Daten. Darunter kann man schonmal grob Mocking verstehen.

image

Allerdings ist es ziemlich umständlich für alles und jeden Test die Umgebung entsprechend durch solche Fake-Implementationen komplett zu mimen. Zudem fehlt etwas die Verbindung zwischen der Mockklasse und dem tatsächlichen Einsatzort.

Die Magie der Frameworks
Wie für fast jedes Problem, gibt es auch hier kleine Helferlein, welche durch Magie (es ähnelt Magie ;) ) dir den kompletten Implementationsaufwand der einzelnen konkreten Klassen abnehmen.
Es gibt in der .NET Welt einige Mocking Frameworks: Moq, Rhino.Mocks, TypeMock usw.
Ich bezieh mich nun auf Rhino.Mocks.

In die Praxis:
Wir haben wieder den ähnlichen Aufbau wie die letzten Male:

image 

Wer genauer hinschaut, sieht, dass es keine konkrete Implementation von IPersonRepository zu sehen ist – keine DummyRepository, auch nicht im Test.

Der Aufbau:
In unserem “PersonFilter” haben wir diesmal nur den “WithAge” Filter. Unser “IPersonRepository” hat eine “GetPersons” Methode.

Den PersonService haben wir nun so implementiert:

namespace RhinoMocks.Services
{
    public class PersonService
    {
        private IPersonRepository PersonRep { get; set; }

        public PersonService(IPersonRepository rep)
        {
            this.PersonRep = rep;
        }

        public IList<Person> GetPersonsByAge(int age)
        {
            return this.PersonRep.GetPersons().WithAge(age).ToList();
        }
    }
}

Im Konstruktur übergeben wir unser PersonRepository und in der GetPersonsByAge Methode fragen wir quasi über unseren Filter das entsprechende Repository ab.

Ich benutze die Rhino.Mocks Version 3.5 – welche die aktuellste ist. Diese einfach referenzieren.

Im Test möchten wir nun genau diese eine Methode testen:

        [TestMethod]
        public void PersonService_GetPersonByAge_Works()
        {
            MockRepository mock = new MockRepository();
            IPersonRepository rep = mock.StrictMock<IPersonRepository>();

            using (mock.Record())
            {
                List<Person> returnValues = new List<Person>()
                {
                    new Person() { Age = 11, Name = "Bob" },
                    new Person() { Age = 22, Name = "Alice" },
                    new Person() { Age = 20, Name = "Robert" },
                    new Person() { Age = 40, Name = "Hans" },
                    new Person() { Age = 20, Name = "Peter" },
                    new Person() { Age = 20, Name = "Oli" },
                };
                Expect.Call(rep.GetPersons()).Return(returnValues.AsQueryable());
            }
            using (mock.Playback())
            {
                PersonService service = new PersonService(rep);
                List<Person> serviceResults = service.GetPersonsByAge(20).ToList();

                Assert.AreNotEqual(0, serviceResults.Count);

                foreach (Person result in serviceResults)
                {
                    Assert.AreEqual(20, result.Age);
                }
            }
        }

Achtung: Wie es genau funktioniert wieß ich auch noch nicht – allerdings tut es ;)

Schauen wir es uns mal genauer an:

            MockRepository mock = new MockRepository();
            IPersonRepository rep = mock.StrictMock<IPersonRepository>();

Dieser Codeschnipsel erstellt uns dynamisch ein Objekt vom Typ “IPersonRepository”.

Jetzt kommt der “Record” Mode:

using (mock.Record())
            {
                List<Person> returnValues = new List<Person>()
                {
                    new Person() { Age = 11, Name = "Bob" },
                    new Person() { Age = 22, Name = "Alice" },
                    new Person() { Age = 20, Name = "Robert" },
                    new Person() { Age = 40, Name = "Hans" },
                    new Person() { Age = 20, Name = "Peter" },
                    new Person() { Age = 20, Name = "Oli" },
                };
                Expect.Call(rep.GetPersons()).Return(returnValues.AsQueryable());
            }

Alles was innerhalb von diesem Using ist, wird “aufgezeichnet”. Als erstes erstelle ich mir meine PersonCollection (mein Umfeld in dem in den Test laufen lassen möchte).
In “Expect.Call” geb ich die Methode an, die aufgerufen wird und was dabei als Returnvalue zurückgegeben wird (in meinem Fall meine PersonCollection).

Im letzten “Playback” Abschnitt:

using (mock.Playback())
            {
                PersonService service = new PersonService(rep);
                List<Person> serviceResults = service.GetPersonsByAge(20).ToList();

                Assert.AreNotEqual(0, serviceResults.Count);

                foreach (Person result in serviceResults)
                {
                    Assert.AreEqual(20, result.Age);
                }
            }

Hier wird das, was wir vorher definiert haben bei den entsprechenden Calls “abgespielt”.
Wie die Magie direkt funktioniert, weiß ich nicht, allerdings wird dynamisch zur Laufzeit dieser Typ generiert:

image

Ergebnis:
Der Test läuft durch und ich hab durch diesen Mock genau nur eine Sache getestet – ob der Service läuft oder nicht. Ob die DB verrückt spielt ist mir an dieser Stelle egal :)

Andere Beispiele:

Ich hab am Anfang ein einfaches “Hello World” Beispiel gefunden. Einige Screencasts findet man hier und ein kleines Wiki hat auch noch ein paar Artikel darüber.

Wenn mein Beispiel komplett falsch ist (oder ich eine falsche Erklärung hab), dann einfach melden – ich arbeite mich erst in diese Materie ein :)

[ Download Democode ]


Kick It auf dotnet-kicks.de
Wenn dir der Blogpost gefallen hat, dann hinterlasse doch einen Kommentar. Wenn du auf dem Laufenden bleiben willst, abonniere unseren RSS Feed oder folge uns auf Twitter.

About the author

Written by Robert Mühsig

Robert Mühsig (@robert0muehsig) ist Webentwickler und beschäftigt sich mit Web-Frameworks (vor allem dem ASP.NET MVC Framework) und scheut sich auch nicht vor Javascript. Ansonsten bloggt er über all jene Probleme, die ihm über den Weg laufen. Seit 2008 ist er Microsoft MVP für ASP.NET und er arbeitet bei der T-Systems Multimedia Solutions GmbH in Dresden. Treffen kann man ihn online via Twitter (@robert0muehsig) oder dieser Seite oder bei der .NET User Group Dresden.

7 Responses

  1. Schön aufgezeigt. Da war mal wieder jemand schneller… Na zum Glück gibt es noch mehr in dem Bereich ;-)

    Auf der .NET Open Space mit dabei?

    Reply
  2. @all: Ich hab gerade mich etwas tiefer damit beschäftigt, dabei haben mir folgende Seiten gut geholfen:
    Stephen Walthers Blog
    Codeproject
    Dimecast

    @Rainer: Ob ich da dabei bin, kann ich noch nicht sagen. Ich schau mal ob es klappen könnte – ist ja quasi um die Ecke.
    Ich bin gespannt wann du dein "Vom Code zur Doku" weitermachst (ich hoffe du wartest nicht auf das ReadYou Project von mir – durch meine eigenen Anforderungen und Ideen verstrick ich mich immer tiefer in Userauthentifizierungsgeschichten (OpenID, CardSpace, "Zermatt"), in TDD (Mocking ;) ) und ASP.NET MVC).

    Reply
  3. Meine Dokumentation werde ich demnächst weiter machen. Mir sind da noch andere wichtige Themen für meine Entwicklung dazwischen gekommen. Du weißt ja, Arbeit und so …

    Reply
  4. schau mal auch auf http://www.pnpguidance.com und http://www.gmbsg.com (<– deutsche Website). Hier sind gute Informationen zu den Themen dabei.

    Reply
  5. Sehr schöner Artikel zu Mocking für einen Einsteiger wie mich ;-) Allerdings ist es bei mir auch ein Zeitproblem, wie so oft…, damit mehr zu machen. Wenn die Arbeit nicht immer dazwischen kommen würde ;-)

    Reply

Comment on this post

Letzte Posts

  • Carriage Return / Neue Zeile in Textareas

    Eine kleine Aufgabe: Jede neue Textzeile (Carriage Return/Wenn man Enter drückt ) in einer Textarea soll ein Element in einer Auflistung sein – wie mach ich das jetzt am einfachsten? Eigentlich ein grundlegendes Element im Web und der Nutzer macht bewusst Absätze – daher wäre es nur gerecht, wenn man das auch entsprechend würdigt. Kleine ...

  • image.png
    Doom, Quake, Wolfenstein & co. Source Code auf GitHub

    id Software, die Macher von Doom, Quake, Wolfenstein & co., stellen regelmäßig ihre älteren Spieltitle als Open Source zur Verfügung. Das Ganze runterzuladen fand ich bisher immer recht mühselig, allerdings gibt es seit kurzer Zeit die Sourcen auch auf GitHub. Darunter Spiele wie Doom 3, Quake 3, Wolfenstein für iOS. Wer also schon immer mal ...

  • image.png
    Twitter Bootstrap 2.0 released & “Release Präsentation”

    Wie bereits vom Twitter Bootstrap Team angekündigt wurde offiziel die Version 2.0 des UI Toolskits “Twitter Bootstrap” veröffentlich. Zudem wurden die Slides, welche bei der Release Party gezeigt wurden auch veröffentlicht: Downloads finden sich auf der Twitter Bootstrap Seite auf GitHub. Wenn dir der Blogpost gefallen hat, dann hinterlasse doch einen Kommentar. Wenn du auf ...

  • image.png
    Javascript zu Dart Translator

    Dart, Google Javascript Alternative, wurde vor ein paar Monaten vorgestellt und die Webentwickler Szene ist noch etwas gespalten, ob Dart nun überflüssig ist oder einfach nur cool und längst überfällig ist. Um die Sprache näher zu erläutern hat Google die grundlegenden Javascript Basics nach Dart übersetzt. Das Ergebnis ist der “Translator”. Der Name mag momentan ...

  • Twitter Bootstrap 2.0–“Beta”

    Twitter Bootstrap, ein UI-Toolkit für Web-Applikationen von Twitter, erscheint (wie bereits berichtet) demnächst in der Version 2.0. Der offizielle Release ist am 31. Januar, allerdings beginnt jetzt laut Mark Otto (einer der Hauptentwickler von Twitter Bootstrap) die intensive Test-Phase. Das heisst, das es nun offiziel auch die 2.0 Dokumentation online gibt. Im Vergleich zur aktuellen ...

Support us!

Facebook