Archiv der Kategorie: Mock Objects

Fallstricke bei NMock mit Klassen

Mit NMock können Attrappen (Mock Objetcs) für Klassen und Interfaces erzeugt werden. Ein deutscher Artikel über NMock ist beim dot.net magazin online abrufbar. Falls Mock Objects für Klassen erstellt werden, müssen einige Dinge beachtet werden.

1. Funktionen und Properties müssen virtual sein
Funktionen und Properties, welche durch das Mock Object ersetzt werden sollen, müssen als

virtual

deklariert werden, da NMock diese sonst nicht überschreiben kann.
NMock wirft normalerweise eine Exception, falls man eine Funktion mit

Expect()

,

ExpectAndReturn()

,

ExpectAndThrow()

oder

ExpectNoCall()

konfiguriert und diese nicht virtual ist.
Wenn aber ein Property nicht als

virtual

deklariert wird und mit

SetupResult()

das Verhalten überschrieben werden soll, wird keine Exception generiert. Es wird aber beim Aufrufen des Properties der Code der zu ersetzenden Klasse aufgerufen:

public class ClassToMock
{
public string Bar
{
get { return "bar"; }
}
}
[TestFixture]
public class TestingClass
{
[Test]
public void MockTest()
{
DynamicMock mock = new DynamicMock(typeof(ClassToMock));
mock.SetupResult("Bar", "foo");
ClassToMock c = (ClassToMock)mock.MockInstance;
Assert.AreEqual("foo", c.Bar);
}
}

Dieser Test schlägt Fehl, da immer noch der Code der Klasse

ClassToMock

aufgerufen wird und somit der Text „bar“ und nicht der Text „foo“, wie eigentlich erwartet, zurückgegeben wird. Sobald das Property als

virtual

deklariert wird, kann der Test erfolgreich ausgeführt werden.

Nicht virtuelle Funktionen können auch nicht durch den „Strict“ Mechanismus überwacht werden. Folgender Test sollte eigentlich fehlschlagen, da es nicht erwartet wird, dass die Funktion

Foo()

aufgerufen wird (kein

Expect...()

). Der Test wird aber erfolgreich ausgeführt.

public class ClassToMock
{
public void Foo() {}
}
[TestFixture]
public class TestingClass
{
[Test]
public void MockTest()
{
DynamicMock mock = new DynamicMock(typeof(ClassToMock));
mock.Strict = true;
ClassToMock c = (ClassToMock)mock.MockInstance;
c.Foo();
mock.Verify();
}
}

Sobald man Foo als

virtual

deklariert schlägt der Test wie erwartet fehl.

2. Wenn Strict benutzt wird muss Finalize als expected angegeben werden
Durch setzten des Attributes

Strict

auf

true

wird NMock angewiesen, dass alle Funktionsaufrufe vorher angemeldet werden müssen. Das gilt auch für den Destruktor, der am Ende der Lebenszeit des Objekts aufgerufen wird.
Da der Destruktor (Methode

Finalize()

) durch den Garbage Collector aufgerufen wird hat man keine Kontrolle darüber, wann der Destruktor aufgerufen wird. Bei den Tests wird er normalerweise nach den eigentlichen Tests aufgerufen. Deshalb wird eine fehlende Finalize-Anmeldung auch nicht als fehlschlagender Test gewertet, es wird aber eine Fehlermeldung auf die Error-Konsole ausgegeben, was nicht sehr Vertrauenserweckend wirkt.
Wenn man die Tests mit NUnit-GUI oder mit dem Visual-Studio-PlugIn TestDriven.NET ausführt ist diese Fehlermeldung nicht sichtbar.
Folgender Code erzeugt die Fehlermeldung:

public class ClassToMock
{
}
[TestFixture]
public class TestingClass
{
[Test]
public void MockTest()
{
DynamicMock mock = new DynamicMock(typeof(ClassToMock));
mock.Strict = true;
ClassToMock c = (ClassToMock)mock.MockInstance;
mock.Verify();
}
}

Wenn der Test mit nunit-console aufgerufen wird, wird am Schluss folgende Fehlermeldung ausgegeben:

Unbehandelte Ausnahme: NMock.VerifyException: Finalize() called too many times
expected:0
but was:<1>
at NMock.Mock.Invoke(String methodName, Object[] args)
at ProxyClassToMock.Finalize()

Wenn man nun aber am Anfang des Codes folgende Zeile einfügt:

mock.Expect("Finalize");

reklamiert die Funktion

Verify()

zu Recht, da der Destruktor zu diesem Zeitpunkt noch nicht aufgerufen wurde.
Man kann die oben angegebene Zeile aber nach den letzten

Verify()

-Aufruf setzten. Dadurch wird das Mock Object auf den Destruktor vorbereitet, welcher dann am Ende des Testes aufgerufen wird. Dieser Code kann z.B. in den

[TearDown]

-Bereich eines Testes geschrieben werden.

Owen Rogers, auch bekannt unter dem Namen exortech, hat diesen Bug auch entdeckt und in seinem Blog beschrieben. Scheinbar hat aber diese Änderung den Weg in den Code von NMock nicht gefunden…