In einem WebAPI Projekt musste ich für die Controllermethoden UnitTests schreiben. Da die Controllermethoden auf eine Datenbank zugreifen und dabei das EntityFramework verwenden, war die Idee naheliegend mit dem Fakesframework die Datenbankzugriffe zu mocken.
Das Testprojekt ist schnell angelegt aber es muss auch das Nuget Package für das Entityframework hinzugefügt werden. (Add-Package EntityFramework) Danach kann für die Dll des EntityFrameworks die Fakes-Assembly erstellt werden. Eine Anleitung wie das Fakesframework zu nutzen ist hier (http://blog.ppedv.de/post/2012/06/24/Unit-Testing-von-SharePoint-Solutions-Das-Fakes-Framework-machts-moglich-Teil-1-Fakes-Framework.aspx) zu finden. Auch für die Dll des WebApi Projektes muss ein Fakesassembly erstellt werden. in dieser DLL Ist des DBContext des EntityModells enthalten und einige Methoden daraus müssen umgeleitet werden.
Nachdem beide Fakeassemblies erstellt wurden kann die Testklasse geschrieben werden. Ich habe das Anlegen des ShimsContext in eine TestInitialize-Methode ausgelagert und das Dispose des ShimsContext wird im Test-Cleanup erledigt.
1 [TestClass()]
2 public class CustomersControllerTests
3 {
4 IDisposable shimContext;
5 bool saveChangesCalled = false;
6
7 [TestInitialize]
8 public void PrepareShim()
9 {
10 shimContext = ShimsContext.Create();
11
12 ShimDbContext.AllInstances.SaveChanges = (ctx) => {
13 saveChangesCalled = true;
14 return 0;
15 };
16
17 ShimNorthwindEntities c = new ShimNorthwindEntities();
18
19 ShimNorthwindEntities.AllInstances.CustomersGet = (ctx) =>
20 {
21 List<Customer> cList = new List<Customer>();
22 cList.Add(new Customer { CustomerID = "12345", CompanyName = "ABC", Country = "Germany" });
23 cList.Add(new Customer { CustomerID = "23456", CompanyName = "DEF", Country = "Austria" });
24 cList.Add(new Customer { CustomerID = "ABCDE", CompanyName = "DEF", Country = "Germany" });
25
26 ShimDbSet<Customer> cust = new ShimDbSet<Customer>();
27 cust.Bind(cList.AsQueryable());
28
29 return cust;
30 };
31 }
32
33 [TestCleanup]
34 public void DisposeShim()
35 {
36 shimContext.Dispose();
37 }
Zunächst wird der ShimsContext erstellt. Danach wird der Methode SaveChanges eine eigene Methode zugewiesen. In späteren Tests muss evtl. abgeprüft werden, ob diese Methode auch vom Controller aufgerufen wurde.
Über ShimNorthwindEntities.AllInstances.CustomerGet wird dem Customers Property eine neue GetMethode hinterlegt. Diese Methode liefert eine Liste von Datenbankeinträgen. Die Liste die zurückgegeben wird, ist ein ShimDbSet-Objekt. Um in dieses Objekt die Liste der Fake-Einträge zu hinterlegen ist die Methode Bind aufzurufen und die Liste mit den Fake-Einträgen zu übergeben. Über diesen Weg, kann jede Tabelle im EntityModell durch eine eigene Get-Methoden ersetzt werden.
Das Erstellen eines Unittests ist dann einfach. Um eine Get Mehode, die nur Daten abrufen soll, zu testen kann folgender Test genutzt werden:
1 [TestMethod()]
2 public void getTest()
3 {
4 CustomersController target = new CustomersController();
5
6 var liste = target.Get("Austria");
7
8 Assert.AreEqual(1, liste.Count);
9 Assert.AreEqual("23456", liste[0].CustomerID);
10 }
Da vor dem Test die TestInitialize-Methode aufgerufen wird, gilt während der Ausführung des Testes der ShimsContext und die Customers Tabelle enthält die vorher definierten Einträge. Damit kann der Test nun unabhängig vom Datenbankinhalt geschrieben und ausgeführt werden.
Um zu Testen ob die SaveChanges Method aufgerufen wird, habe ich einen Test für die PUT Methode geschrieben. In diesem wird die Variable saveChangesCalled verwendet um zu Prüfen ob der Aufruf stattgefunden hat.
1 [TestMethod]
2 public void TestPut()
3 {
4 CustomersController t = new CustomersController();
5 t.Put(new Customer { CustomerID = "12345" });
6
7 Assert.IsTrue(saveChangesCalled);
8 }