Ein Maulwurf in Visual Studio 11: Das Microsoft Fakes Isolation Framework

Mittlerweile ist die Verfügbarkeit der Visual Studio 11 Beta hinreichend bekannt. Was aber die wenigsten wissen: Unter der Haube steckt noch einiges mehr, als Microsoft aktuell bewirbt. Und dazu gehört z.B. Microsoft Moles.

Pardon. Ich meine natürlich das Microsoft Fakes Isolation Framework. Noch nie davon gehört? Das ist kaum verwunderlich. Sucht man danach im Internet, trifft man aktuell höchstens auf zwei Referenzen. Zum einen die Landing Page auf Microsoft Research für Pex & Moles und zum anderen auf die vorläufige Hilfe zu Visual Studio 11 auf MSDN. Dass letztere kaum auffindbar ist, wenn nicht gezielt danach gesucht wird, leuchtet ein. Und da die Hilfe zum jetzigen Zeitpunkt weder vollständig noch korrekt ist, stellt TOP TECHNOLOGIES hiermit das Microsoft Fakes Isolation Framework kurz vor und demonstriert mit einer funktionierenden Solution, wie man damit jetzt schon umgehen kann.

Am Ende dieses Artikels gibt es eine Zip-Datei mit der Solution zum Herunterladen.

Ein kurzer Hinweis für Leser, die mit den Begriffen Moles und Fakes nichts anfangen können: Die Idee hinter diesen Frameworks ist es, speziell für Unit-Tests Abhängigkeiten durch Attrappen zu ersetzen, um die Tests auf den Code konzentrieren zu können, der getestet werden soll. Gute Beispiele dafür sind Methoden, die neben der fachlichen Verarbeitung von Daten diese z.B. zuvor aus einer Datenbank oder einer Datei beziehen müssen. Ein ebenfalls häufig genutzter Begriff für die Bereitstellung solcher Attrappen ist auch “Mocking”.

Das Beispiel hier ist der Microsoft Fakes Hilfe entnommen und zeigt, wie eine Methode getestet werden kann, die eine Abhängigkeit zum Dateisystem hat, ohne dabei tatsächlich auf Dateien zuzugreifen (wovon nicht nur aus Performancegründen abzuraten wäre).

Demo

Zunächst wird in Visual Studio 11 ein C# Klassenbibliotheksprojekt mit dem Namen FakesDemo angelegt. Die Datei class1.cs kann gelöscht werden. Dann wird ein neues Interface mit dem Namen IFileSystem hinzugefügt:

namespace FakesDemo
{
    public interface IFileSystem
    {
        string ReadAllText(string fileName);
    }
}

 

Die Implementation der Schnittstelle greift wie erwartet auf System.IO.File zu, um den Inhalt einer angegebenen Datei auszulesen und zurückzugeben:

using System.IO;

namespace FakesDemo
{
    public class FileSystem : IFileSystem
    {
        public string ReadAllText(string fileName)
        {
            return File.ReadAllText(fileName);
        }
    }
}

 

Eine statische Klasse mit Hilfsmethoden für die Arbeit mit Dateien könnte nun beispielsweise eine Methode enthalten, die überprüft, ob eine Datei leer ist. Um dabei eine möglichst lose Kopplung zu erreichen, wird der Methode ein Objekt übergeben, das die IFileSystem-Schnittstelle implementiert:

using System;

namespace FakesDemo
{
    public static class FileHelper
    {
        public static bool IsEmpty(IFileSystem fs, string f)
        {
            var content = fs.ReadAllText(f);
            return String.IsNullOrEmpty(content);
        }
    }
}

 

Und hier wird nun deutlich: Ein Unit-Test zu der Methode IsEmpty hätte eine Abhängigkeit zu einer Implementierung des IFileSystem Interfaces und damit den Zugriff auf eine physikalische Datei zur Folge. An dieser Stelle kommt Microsoft Fakes zum Zuge.

Das Unit-Test Projekt

Als erstes wird der Solution ein Unit-Test Projekt mit dem Namen FakesDemo.Tests hinzugefügt und die UnitTest1.cs Datei entfernt. Dann wird eine Solution Referenz zu dem FakesDemo Projekt hinzugefügt. Während das normalerweise notwendig ist, um auf die zu testenden Klassen zuzugreifen, benötigt Fakes die Referenz, um daraus die Stubs zu generieren. Für Fakes wird dazu eine weitere Referenz benötigt, die auf die DLL Microsoft.QualityTools.Testing.Fakes.dll im Verzeichnis C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\ReferenceAssemblies\v4.0 verweist.

Microsoft Fakes wird über MSBuild gesteuert. Die Dokumentation erklärt hier den Einsatz von Xml-Dateien, um Fakes zu konfigurieren. Dort heißt es, Xml-Dateien mit dem Suffix .fakes würden vom MSBuild Target erkannt und auf deren Basis würden dann Stubs für die angegebenen Assemblies und Namespaces generiert und als Referenz dem Projekt hinzugefügt.

The generation of stub types is configured in an XML file that has the .fakes file extension. The Fakes framework integrates in the build process through custom MSBuild tasks and detects those files at build time. The Fakes code generator compiles the stub types into an assembly and adds the reference to the project.

In der aktuellen Beta ist da allerdings noch der ein oder andere manuelle Handgriff erforderlich.

Zunächst wird eine neue Xml-Datei mit dem Namen FakesDemo.fakes dem Projekt hinzugefügt:

<Fakes xmlns="http://schemas.microsoft.com/fakes/2011/">
  <Assembly Name="FakesDemo"/>
  <StubGeneration>
    <Clear />
    <Add Namespace="FakesDemo!" />
  </StubGeneration>
</Fakes>

 

imageNun ist es wichtig, die Datei im Solution Explorer anzuklicken und in den Properties die BuildAction auf Fakes umzustellen (dieser Hinweis fehlt aktuell noch in der Dokumentation).

Wenn die Solution nun gebaut wird, zeigt das Build-Log in etwa folgendes:

1>------ Rebuild All started: Project: FakesDemo, Configuration: Debug Any CPU ------
1>  FakesDemo -> C:\Demos\FakesDemo\FakesDemo\bin\Debug\FakesDemo.dll
2>------ Rebuild All started: Project: FakesDemo.Tests, Configuration: Debug Any CPU ------
2>  Microsoft Fakes v11.0.50214.1 - .NET v4.0.30319
2>  Copyright (c) Microsoft Corporation 2007-2010. All rights reserved.
2> 
2>  fakes
2>    code generation
2>      stubs generation
2>      shims generation
2>    compiling
2>  fakes generator 0 errors, 0 warnings
2>  FakesDemo.Tests -> C:\Demos\FakesDemo\FakesDemo.Tests\bin\Debug\FakesDemo.Tests.dll
========== Rebuild All: 2 succeeded, 0 failed, 0 skipped ==========

In der aktuellen Version fügt der MSBuild Task die generierte DLL leider noch nicht automatisch als Referenz hinzu. Daher muss das nun manuell nachgeholt werden. Unterhalb des FakesDemo.Tests Verzeichnis findet sich nach dem Build ein Unterverzeichnis mit dem Namen FakeAssemblies. Die generierte Datei FakesDemo.Fakes.dll wird als Referenz dem FakesDemo.Tests Projekt hinzugefügt. In dieser Assembly finden sich nun alle generierten Shims und Stubs.

image

Der Test

Um nun die Funktionsweise der FileHelper Methode IsEmpty zu testen, wird Im Unit-Test Projekt ein Unit-Test mit dem Namen FileIsNotEmpty erstellt. Mehr Informationen dazu finden sich ebenfalls in der Fakes Hilfe auf MSDN.

using FakesDemo.Fakes;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace FakesDemo.Tests
{
    [TestClass]
    public class FakesDemoLibTests
    {
        [TestMethod]
        public void FileIsNotEmpty()
        {
            var file = "MyFile.txt";
            var content = "hello world";

            var fileSystem = new StubIFileSystem()
            {
                ReadAllTextString = filename =>
                {
                    Assert.AreEqual(filename, file);
                    return content;
                }
            };

            bool result = FileHelper.IsEmpty(fileSystem, file);

            Assert.IsFalse(result);
        }
    }
}
 

 

imageDer Aufruf FileHelper.IsEmpty(fileSystem, file) verwendet nun ein Delegate, das initialisiert über StubIFileSystem (generiert von Fakes) das Interface IFileSystem mit einer Attrappe belegt. Diese Attrappe lässt die Methode ReadAllText nun immer den Inhalt hello world zurückliefern. Es ist kein physischer Dateizugriff erfolgt.

Nach einem Rebuild lässt sich der Test z.B. im Unit Test Explorer via Run All ausführen.

Zusammengefasst

Mit den aktuell verfügbaren Informationen auf MSDN ist schon eine Menge anzufangen und Microsoft zeigt mit Fakes einmal mehr, wie sich im Laufe der Zeit die Arbeit im Research Team nicht nur auszahlt sondern auch Bestandteil produktiver Werkzeuge werden kann. Nikolai Tillmann und Peli de Halleux haben in den letzten zwei Jahren eng mit dem Visual Studio zusammengearbeitet, um das möglich zu machen.

Und für alle Early Adaptors gilt: Um mit den Beispielen arbeiten zu können sind folgende drei Schritte notwendig:

  1. Die BuildAction der .fakes Datei manuell auf Fakes einstellen
  2. Die Referenz zur Microsoft.QualityTools.Testing.Fakes.dll hinzufügen
  3. Nach dem ersten Build die generierte .Fakes.dll manuell hinzufügen

Weitere Informationen darüber, was für Möglichkeiten sich mit Microsoft Fakes ergeben, nach welchem Schema die Stubs und Shims vom Framework benannt werden und welche Filtermöglichkeiten es bei der Konfiguration gibt, lassen sich der Hilfe auf MSDN entnehmen.

Diese kleine Demo diente in erster Linie dazu, die Klippen zu umschiffen, die – Stand heute – noch der Beta geschuldet sind. In den nächsten Wochen werden sich zu diesem Artikel zudem noch weitere Beispiele gesellen, um in das Thema Mocking im Allgemeinen und Microsoft Fakes im Speziellen weiter einzugehen.

Von Experten für Experten

Wer sich mit dem Thema Pex & Moles bereits auskennt, den wird vielleicht folgendes interessieren: Pex arbeitete intern auf Basis eines eigenen Profilers (“Extended Reflection”). Fakes hingegen benutzt den Visual Studio Profiler, der ursprünglich für IntelliTrace entwickelt wurde. Das zeigt sehr deutlich das Bestreben bei Microsoft, ehemalige Research Projekte sauber in die produktiven Werkzeuge zu integrieren. Als nächstes ist darüber hinaus auch geplant, eine neue Version von Pex zu veröffentlichen, die nahtlos mit Fakes zusammenarbeitet, so dass kein separates Moles mehr notwendig sein wird.

[UPDATE]

Eine weitere sehr bequeme Möglichkeit, über die UI des Visual Studio Fakes hinzuzufügen, ist direkt über das Kontextmenü einer Referenz. Ein Beispiel im Zusammenhang mit SharePoint findet man im Blog bei Richard Fennell.

 

FakesDemo.zip (12,30 kb)

Pingbacks and trackbacks (2)+

Comments are closed

Über die Autoren

Christian Jacob ist Leiter des Geschäftsbereiches Softwarearchitektur und -entwicklung und zieht als Trainer im Kontext der .NET Entwicklung sowie ALM-Themen Projekte auf links.

Marcus Jacob fokussiert sich auf die Entwicklung von Office-Addins sowie Windows Phone Apps und gilt bei uns als der Bezwinger von Windows Installer Xml.

Martin Kratsch engagiert sich für das Thema Projektmanagement mit dem Team Foundation Server und bringt mit seinen Java- und iOS-Kenntnissen Farbe in unser ansonsten von .NET geprägtes Team.

Aktuelle Kommentare

Comment RSS