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
deklariert werden, da NMock diese sonst nicht überschreiben kann.
NMock wirft normalerweise eine Exception, falls man eine Funktion mit
,
,
oder
konfiguriert und diese nicht virtual ist.
Wenn aber ein Property nicht als
deklariert wird und mit
das Verhalten überschrieben werden soll, wird keine Exception generiert. Es wird aber beim Aufrufen des Properties der Code der zu ersetzenden Klasse aufgerufen:
{
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
aufgerufen wird und somit der Text „bar“ und nicht der Text „foo“, wie eigentlich erwartet, zurückgegeben wird. Sobald das Property als
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
aufgerufen wird (kein
). Der Test wird aber erfolgreich ausgeführt.
{
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
deklariert schlägt der Test wie erwartet fehl.
2. Wenn Strict benutzt wird muss Finalize als expected angegeben werden
Durch setzten des Attributes
auf
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
) 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:
{
}
[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:
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:
reklamiert die Funktion
zu Recht, da der Destruktor zu diesem Zeitpunkt noch nicht aufgerufen wurde.
Man kann die oben angegebene Zeile aber nach den letzten
-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
-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…