Jetzt finde ich endlich mal wieder die Zeit, ein paar Zeilen zu schreiben und wie bereits vor einiger Zeit versprochen, möchte ich hier einmal ein paar Gedanken zusammen fassen, die mir während der letzten Wochen im Zusammenhang mit meiner Projektgruppe gekommen sind. Und zwar habe ich in den letzten Woche einige Tests für WCF Services geschrieben. Die eigentliche Aufgabe bestand darin, Unit Tests für die Services zu schreiben. Die Unit Tests, sollten, wie es eigentlich üblich ist, dazu dienen die einzelnen Klassen bzw. Assemblies isloiert von ihren Abhängigkeiten zu testen. Das stellte sich als schwieriger raus, als zunächst angenommen:
Zunächst einmal bieten WCF Services, im Gegensatz zu den älteren ASP.Net WebServices, den Vorteil, dass Sie bereits als Libraries vorliegen. So ist es einfach möglich, diese Unit Tests zu unterziehen.
-
[Test]
-
public void InvokeSayHello() {
-
string hello = service.SayHello("World");
-
Assert.AreEqual("Hello World!", hello);
-
}
Nachdem ich einige Unit Tests dieser Art geschrieben hatte, musste ich feststellen, dass diese Methode einen Nachteil hat. Es ist kaum möglich solche Unit Tests zu schreiben, wenn der zu testende Service WCF-spezifischen Code benutzt. Wenn z.B. versucht wird auf den aktuellen OperationContext (OperationContext.Current) zuzugreifen, dürfte ein Unit Test in der Regel fehlschlagen. Da das Property static ist, führt das natürlich dazu, dass der Wert null ist, wenn der Service nicht von WCF gehostet wird.
Bemerkung am Rande: Der eine oder andere mag der Ansicht sein, dass dies eine starke Einschränkung ist. Ich sehe hier aber eher einen hilfreichen Ansatz um SoC zu unterstützen. Services sollten schließlich Geschäftslogik enthalten und nicht irgendwelche anderen Dinge implementieren, die damit nichts zu tun haben (z.B. Binding- oder Transportlogik). Also, WCF-spezifischer Code hat, meiner Meinung nach, nichts in der Service-Implementierung zu suchen, sondern gehört ins Service Interface Layer.
Man wird also ab irgendeinem Punkt nicht mehr darum herum kommen, einen Service im WCF zu hosten und Tests gegen diesen zu schreiben. Selbst wenn man SoC bis zur Perfektion treibt (irgendwie habe ich gerade ein Grinsen im Gesicht), ist dies z.B. spätestens der Fall, wenn man eigene bzw. domänen-spezifische ServiceAuthorizationManager oder das Verhalten bei bestimmten Bindings etc. testen will oder muss.
Dies könnte dann so aussehen:
-
public class MyService : IMyService {
-
#region IMyService Members
-
-
public string SayHello(string name) {
-
return "Hello " + name + "!";
-
}
-
-
#endregion
-
}
-
-
[TestFixture]
-
public class MyServiceTest {
-
-
private static ServiceHost sh;
-
-
[TestFixtureSetUp]
-
public static void TestFixtureSetUp() {
-
-
MyServiceTest.sh.Open();
-
-
}
-
-
[TestFixtureTearDown]
-
public static void TestFixtureTearDown() {
-
-
MyServiceTest.sh.Close();
-
-
}
-
-
[Test]
-
public void InvokeSayHello()
-
{
-
-
{
-
string hello = client.SayHello("World");
-
Assert.AreEqual("Hello World!", hello);
-
}
-
-
}
-
}
Anmerkung: Für einen zustandsbehafteten Service sollten der Service in der SetUp-Methode instanziert werden und nicht in TestFixtureSetUp.
Ob man bei solchen Tests noch von Unit Tests sprechen kann ist aus meiner Sicht allerdings mehr als fraglich. Hier überschreitet man schnell die Schwelle zu Integrations Tests. Dies ist vor allem dann der Fall, wenn der zu testende Service nicht nur einfache Berechnungen oder Transformation, wie in obigem Fall, vornimmt, sondern z.B. ein zustandsbehafteter Dienst ist, der Datenbanken nutzt oder evtl. von weiteren Services abhängig ist.
An dieser Stelle bietet die SOA-Architektur natürlich den Vorteil andere Services einfach durch Platzhalter, z.B. Mock Objects, zu ersetzen. Stellt sich nur die Frage wie es weiter geht, wenn das Verhalten des zu testenden Services von den Ergebnissen, oder sogar Exceptions, anderere Services abhängig ist. Es dürfte wohl etwas kompliziert werden, alle möglichen Verhaltensweisen dieser Services nachzubilden. Aber das ist ein anderes komplexes Thema, über das ich heute nicht mehr nachdenken will.