Tanpohp

Tag: NUnit

Simple Mocking – Framework zum erstellen von Mocks

by on Apr.14, 2014, under Test

Seit einiger Zeit beschäftige ich mich etwas mehr mit Unittest, vor allem auf Basis von NUnit. Unittest typisch sollen in jedem Fall nur einzelne Klassen getestet werden, um sehr genau Eingabewerte festlegen und Ausgabewerte überprüfen zu können. Leider gibt es immer wieder Konstellationen, in denen Klassen von anderen Klassen abhängig sind, was deren unabhängigen Test nahezu unmöglich macht. Um diesem Umstand zu begegnen werden die Abhängigkeiten durch Schnittstellen gekapselt und entsprechende Mocks geschrieben, die genau definierte Werte an die Klasse liefern. Im Folgenden ein Beispiel, in dem eine User-Factory ein IUser erstellen soll, dessen Daten diese von einem IDataReader bekommt.

public interface IUser{
    string Name {get;}
    string City {get;}
    int Age {get;}
}
public interface IDataReader{
    int GetInt32Value(string id);
    short GetInt16Value(string id);
    string GetStringValues(string id);
    ...
    Vector4 GetVector4(string id);
    ...
}
public static class UserFactory{
    public static IUser CreateFromDataReader(IDataReader reader){
        ...
    }
}

Anstelle der statischen Factory hätte man hier auch das Pattern Abstrakt Fabrik anwenden können und das IDataReader Interface hätte womöglich auch besser gestallten werden können. Angenommen dies wäre die Ausgangssituation und ich möchte die CreateFromDataReader Methode mit verschiedenen Usern prüfen. So müsste ich für jeden Test, einen Mock schreiben welche das IDataReader Interface implementiert und mir genau definierte Werte liefert. Wie die Definition des Interfaces schon andeutet, hat der IDataReader sehr viele Methoden, die zwar nicht benötigt würden, aber dennoch (zumindest leer) implementiert werden müssten.  Sehr viel sich wiederholende Arbeit.  Eigentlich wäre an dieser Stelle nur notwendig,  zwei der vielen Methoden zu implementieren:

string GetStringValue(string id);
int GetInt32Value(string id);

Um diesen Missstand zu begegnen gibt es zwei Möglichkeiten.

  1. Man benutzt Codegeneratoren um all die Methoden zumindest leer generieren zu lassen. Dann werden nur die Teile implementiert, die tatsächlich notwendig sind.
  2. Man nutzt Mocking-Frameworks, welche die Methoden implementieren und nur noch angepasst werden müssen.

Die erste Variante hat gravierende Nachteile: Zum Einen  gibt es eine Menge Code der nicht benötigt wird, aber im Falle eines Fehlers gelesen werden muss, zum Anderen ist es damit nicht Möglich das AAA-Test Pattern (Arrange, Act, Assert) umzusetzen.
Anhand von Simple.Mocking soll deshalb die zweite Methode vorgestellt werden. Dazu müssen wir einige Dinge über das Verhalten von UserFactory wissen:

  1. Alle Daten im späteren IUser Objekt sollen aus dem IDataReader kommen.
  2. Der IDataReader hat sein Daten aus einer anderen Datenquellen (z.B. Datenbank). Die Daten sind über die 3 Ids name, age und city ansprechbar.
  3. Die Reihenfolge in welche die Daten vom IDataReader abgefragt werden, spielen keine Rolle. (Bei einer Deserialisierung aus einer Datei ist die Reihenfolge wichtig und könnte ebenfalls mittels Simple Mocking festgelegt werden.)

Daraus kann nun folgender Test erstellt werden:

[Test]
public class UserFactoryTest {
   [Test]
   public void TestCreateUserFromDataReader(){
       //Arrange
       var userDataReader = Mock.Interface<IDataReader>(); 
       Expect.MethodCall(()=> userDataReader.GetStringValue("name").Return("John");
       Expect.MethodCall(()=> userDataReader.GetStringValue("city").Return("Bahia");
       Expect.MethodCall(()=> userDataReader.GetInt32Value("age").Return("35"));
       //act
       var user = UserFactory.CreateFromUserDataReader(userDataReader);
       //assert
       Assert.Equals("John", user.Name);
       Assert.Equals("Bahia", user.City);
       Assert.Equals(35, user.Age);
   }
}

Als erstes wird der Mock definiert:

var userDataReader = Mock.Interface<IDataReader>();

Im Anschluss werde die einzelnen Methoden-Aufrufe in definiert:

  1. Welche Methode wird aufgerufen.
  2. Welcher Parameter hat die Methode.
  3. Welchen Rückgabewert soll bei dieser Methoden geliefert werden.

Die Definition eines Mock beschränkt sich dadurch auf genau die Methoden, welche ich im Test auch tatsächlich benutzen möchte. Die Verwendung nicht gemockter Methoden erzeugt eine ExpectationException, ebenso wie das aufrufen der gemockten Methoden mit anderen Parametern.

Darüber hinaus bietet Simple Mocking noch einige mehr Features.

  • Prüfen der Reihenfolge von Aufrufe von Methoden/Properties.
  • Prüfen mittels Wildcard Paramtern: Any<T>.Value.Matching(a => Predicate(a))
  • Limitierung der Anzahl von Aufrufen.
  • Definieren von Exceptions die geworden werden.
  • Mocken von Interfaces, Delegaten.

Weitere Information im Getting-Startet Teil des Projektes.

Comments Off on Simple Mocking – Framework zum erstellen von Mocks :, more...

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!

Visit our friends!

A few highly recommended friends...