Izolace aplikace od ostatních sestavení pomocí překrytí za účelem testování částí
Shim typy jsou jedním ze dvou technologií, které Microsoft Fakes Framework umožňují snadno izoluje komponenty v testu z prostředí.Překryvné ovladače přesměrovat volání specifické metody pro kód, který napíšete jako součást vašeho testu.Mnoho metod vracet odlišné výsledky závislé na vnějším podmínkám, ale doplňkový kód je pod kontrolou vaše zkušební a konzistentní výsledky při každém volání můžete vrátit.Jednodušší testy k zápisu.
Překryvné ovladače slouží k oddělení kódu od sestavení, které nejsou součástí vašeho řešení.Pro izolování součásti řešení od sebe, doporučujeme použít kódy.
Přehled a Stručná pokyny naleznete v tématuIzolace testovaného kódu pomocí zástupného rozhraní Microsoft
Požadavky
- Visual Studio Ultimate
Viz Video (1 h 16): testování Un-testable kódem předstírá v aplikaci Visual Studio 2012
V tomto tématu
Zde je, co se dozvíte v tomto tématu:
Přidání sestavení předstírá
Použít ShimsContext
Zápis zkoušky s Překryvné ovladače
Překryvné ovladače pro různé druhy metod
Přistupuje k zjištění prostředí
Volání metody původní z překrytí metody
Příklad: Y2K bug
Zvažte metody, která vyvolá výjimku v 1. leden 2000:
// code under test
public static class Y2KChecker {
public static void Check() {
if (DateTime.Now == new DateTime(2000, 1, 1))
throw new ApplicationException("y2kbug!");
}
}
Testování tato metoda je zvláště problematické, protože program závisí na DateTime.Now, metoda, která závisí na počítači uživatele hodiny, prostředí závislé, není deterministický metoda.Kromě toho DateTime.Now je statická vlastnost, tak zde nelze použít typ se zakázaným inzerováním.Tento problém je vykazující vydání izolace v testování: programy, které přímo volat do databáze rozhraní API komunikovat s webovými službami a podobně jsou obtížně Jednotkový test, protože jejich logiku závisí na životní prostředí.
Toto je použití typů překrytí.Překrytí typy poskytují mechanismus pro detour jakékoli metody .NET delegát definované uživatelem.Překrytí se kód generovaný generátorem předstírá a delegátů, které nazýváme typy překrytí, používají k určení nové implementace metody.
Následující test ukazuje, jak použít typ překrytí ShimDateTime, chcete-li zadat vlastní implementace DateTime.Now:
//unit test code
// create a ShimsContext cleans up shims
using (ShimsContext.Create()
// hook delegate to the shim method to redirect DateTime.Now
// to return January 1st of 2000
ShimDateTime.NowGet = () => new DateTime(2000, 1, 1);
Y2KChecker.Check();
}
Jak použít Překryvné ovladače
Přidání sestavení předstírá
V Průzkumníku řešení rozbalte projektu Jednotka test odkazy.
- Pokud pracujete v jazyce Visual Basic, musíte vybrat Zobrazit všechny soubory v panelu nástrojů Průzkumníka řešení, chcete-li zobrazit seznam odkazů.
Výběr sestavení, které obsahuje definice tříd, pro které chcete vytvořit Překryvné ovladače.Chcete-li například pokud chcete shim DateTime, vyberte System.dll
V místní nabídce zvolte Přidat sestavení Fakes.
Použít ShimsContext
Používáte-li překrytí typy v rámci zkušební jednotky, musí být testovací kód v ShimsContext k řízení životnosti vaší Překryvné ovladače.Jsme neměl vyžadují-li to, vaše Překryvné ovladače by poslední až do vypnutí domény AppDomain.Nejjednodušší způsob, jak vytvořit ShimsContext je pomocí statické Create() metoda, jak je znázorněno v následujícím kódu:
//unit test code
[Test]
public void Y2kCheckerTest() {
using(ShimsContext.Create()) {
...
} // clear all shims
}
Je důležité správně nakládat každý kontext překrytí.Jako pravidlem, vždy volat ShimsContext.Create uvnitř using příkaz k zajištění řádné vymazání registrované Překryvné ovladače.Například může zaregistrovat překrytí pro zkušební metoda, která nahrazuje DateTime.Now metodu delegáta, která vždy vrátí první z ledna 2000.Pokud jste zapomněli zrušte registrované překrytí ve zkušební metody, zbytek zkušební jízdy vždy vrátí hodnotu první části dne jako DateTime.Now.Může se jednat o suprising a matoucí.
Napsat test s Překryvné ovladače
Do kódu test, vložte detour pro metodu, která má být falešné.Příklad:
[TestClass]
public class TestClass1
{
[TestMethod]
public void TestCurrentYear()
{
int fixedYear = 2000;
using (ShimsContext.Create())
{
// Arrange:
// Detour DateTime.Now to return a fixed date:
System.Fakes.ShimDateTime.NowGet =
() =>
{ return new DateTime(fixedYear, 1, 1); };
// Instantiate the component under test:
var componentUnderTest = new MyComponent();
// Act:
int year = componentUnderTest.GetTheCurrentYear();
// Assert:
// This will always be true if the component is working:
Assert.AreEqual(fixedYear, year);
}
}
}
<TestClass()> _
Public Class TestClass1
<TestMethod()> _
Public Sub TestCurrentYear()
Using s = Microsoft.QualityTools.Testing.Fakes.ShimsContext.Create()
Dim fixedYear As Integer = 2000
' Arrange:
' Detour DateTime.Now to return a fixed date:
System.Fakes.ShimDateTime.NowGet = _
Function() As DateTime
Return New DateTime(fixedYear, 1, 1)
End Function
' Instantiate the component under test:
Dim componentUnderTest = New MyComponent()
' Act:
Dim year As Integer = componentUnderTest.GetTheCurrentYear
' Assert:
' This will always be true if the component is working:
Assert.AreEqual(fixedYear, year)
End Using
End Sub
End Class
Názvy tříd překrytí se skládají pomocí prefixu Fakes.Shim k původnímu názvu typu.
Shims práce vložením detours do kódu aplikace zkoušeného.Vždy, když dojde k volání metody původní, provede systém předstírá detour, tak, aby namísto volání skutečné metody, se nazývá kód překrytí.
Všimněte si, že detours se vytvářejí a odstranit v době běhu.Je nutné vytvořit detour během doby existence ShimsContext.Když je uvolněn, jsou odebrány žádné Překryvné ovladače, který jste vytvořili v době, kdy byl aktivní.Nejvhodnějším postupem je uvnitř using prohlášení.
Může se zobrazit sestavení chybová zpráva, že obor názvů předstírá neexistuje.Tato chyba se někdy zobrazí, pokud existují jiné chyby kompilace.Jiné chyby opravit a bude zmizí.
Překryvné ovladače pro různé druhy metod
Překrytí typy umožňují nahradit jakékoli metody rozhraní .NET, včetně statických metod nebo jiných virtuálních metod, s vlastní delegáti.
Statické metody
Vlastnosti, které chcete připojit Překryvné ovladače na statické metody jsou umístěny v typu překrytí.Každá vlastnost má pouze setter metody, které lze připojit k cílové metodě delegáta.Například mít třídu MyClass pomocí statické metody MyMethod:
//code under test
public static class MyClass {
public static int MyMethod() {
...
}
}
Doporučujeme připojit překrytí na MyMethod , vždy vrátí 5:
// unit test code
ShimMyClass.MyMethod = () =>5;
Instance metody (všechny instance)
Podobně pro statické metody lze instanční metody shimmed pro všechny instance.Vlastnosti, které chcete připojit tyto Překryvné ovladače jsou umístěny do vnořeného typu s názvem AllInstances k záměně.Například mít třídu MyClass s metodou instance MyMethod:
// code under test
public class MyClass {
public int MyMethod() {
...
}
}
Můžete připojit překrytí na MyMethod , vždy vrátí hodnotu 5, bez ohledu na to, instance:
// unit test code
ShimMyClass.AllInstances.MyMethod = () => 5;
Struktura generovaný typ ShimMyClass vypadá podobně jako následující kód:
// Fakes generated code
public class ShimMyClass : ShimBase<MyClass> {
public static class AllInstances {
public static Func<MyClass, int>MyMethod {
set {
...
}
}
}
}
Všimněte si, že Fakes předává instance modulu runtime v tomto případě jako první argument delegáta.
Instanční metody (pro jednu instanci modulu runtime)
Instanční metody mohou shimmed také různé delegáty, založené na přijímač volání.To umožňuje stejnou metodu instance mají různé chování každou instanci typu.Vlastnosti, které chcete nastavit tyto Překryvné ovladače jsou metody instance typu překrytí, sám.Každý typ instance překrytí souvisí také s raw instance typu shimmed.
Například mít třídu MyClass s metodou instance MyMethod:
// code under test
public class MyClass {
public int MyMethod() {
...
}
}
Doporučujeme nastavit dva typy překrytí MyMethod tak, že první z nich vždy vrátí 5 a druhý vždy vrátí 10:
// unit test code
var myClass1 = new ShimMyClass()
{
MyMethod = () => 5
};
var myClass2 = new ShimMyClass { MyMethod = () => 10 };
Struktura generovaný typ ShimMyClass vypadá podobně jako následující kód:
// Fakes generated code
public class ShimMyClass : ShimBase<MyClass> {
public Func<int> MyMethod {
set {
...
}
}
public MyClass Instance {
get {
...
}
}
}
Skutečné shimmed typu instance je přístupný prostřednictvím vlastnosti Instance:
// unit test code
var shim = new ShimMyClass();
var instance = shim.Instance;
Typ překrytí má také implicitní převod typu shimmed, je obvykle jednoduše použít překrytí typu, jako je:
// unit test code
var shim = new ShimMyClass();
MyClass instance = shim; // implicit cast retrieves the runtime
// instance
Konstruktory
Konstruktory mohou shimmed také chcete-li připojit typy překrytí na budoucí objekty.Každému konstruktoru je vystavena jako statická metoda konstruktoru typu překrytí.Například mít třídu MyClass s konstruktoru, přičemž celá čísla:
// code under test
public class MyClass {
public MyClass(int value) {
this.Value = value;
}
...
}
Nastavení typu překrytí konstruktoru tak, aby každé budoucí instanci vrátí -5, když je vyvolán getter hodnotu, bez ohledu na hodnotu v konstruktoru:
// unit test code
ShimMyClass.ConstructorInt32 = (@this, value) => {
var shim = new ShimMyClass(@this) {
ValueGet = () => -5
};
};
Všimněte si, že každý typ překrytí poskytuje dva konstruktory.Výchozí konstruktor /t čerstvé instance je potřeba při konstruktoru, přičemž shimmed instance, jako argument by měl být používán pouze Překryvné ovladače konstruktoru:
// unit test code
public ShimMyClass() { }
public ShimMyClass(MyClass instance) : base(instance) { }
Struktura generovaný typ ShimMyClass se podobá followoing kód:
// Fakes generated code
public class ShimMyClass : ShimBase<MyClass>
{
public static Action<MyClass, int> ConstructorInt32 {
set {
...
}
}
public ShimMyClass() { }
public ShimMyClass(MyClass instance) : base(instance) { }
...
}
Základní členové
Vytvořením doplňkový kód pro základní typ a předáním instance podřízené jako parametr do konstruktoru třídy základní překrytí je přístupná překrytí vlastnosti základní členy.
Například mít třídu MyBase s metodou instance MyMethod a podtyp MyChild:
public abstract class MyBase {
public int MyMethod() {
...
}
}
public class MyChild : MyBase {
}
Doporučujeme nastavit překrytí z MyBase vytvořením nového ShimMyBase překrytí:
// unit test code
var child = new ShimMyChild();
new ShimMyBase(child) { MyMethod = () => 5 };
Všimněte si, že podřízený typ překrytí je implicitně převeden na podřízené instance, kdy předaný jako parametr do konstruktoru základní překrytí.
Struktura generovaný typ, ShimMyChild a ShimMyBase se podobá následující kód:
// Fakes generated code
public class ShimMyChild : ShimBase<MyChild> {
public ShimMyChild() { }
public ShimMyChild(Child child)
: base(child) { }
}
public class ShimMyBase : ShimBase<MyBase> {
public ShimMyBase(Base target) { }
public Func<int> MyMethod
{ set { ... } }
}
Statické konstruktory
Typy překrytí vystavit statickou metodu StaticConstructor k shim statického konstruktoru typu.Protože statické konstruktory jsou spouštěny po pouze, je třeba zajistit, aby překrytí je nakonfigurován, každý člen typu se před přístupem k.
Finalizační metody
Předstírá nepodporuje finalizační metody.
Soukromé metody
Generátor kódu předstírá vytvoří vlastnosti překrytí pro soukromé metody, které mají pouze viditelné typy v podpisu, i.e. typy parametrů a návratový typ viditelné.
Vazba rozhraní
Při shimmed typ implementuje rozhraní, posílá generátor kódu metodu, která umožňuje vytvořit vazbu všech členů z rozhraní najednou.
Například mít třídu MyClass , která implementuje IEnumerable<int>:
public class MyClass : IEnumerable<int> {
public IEnumerator<int> GetEnumerator() {
...
}
...
}
Jsme shim implementace IEnumerable<int> v MyClass voláním metody Bind:
// unit test code
var shimMyClass = new ShimMyClass();
shimMyClass.Bind(new List<int> { 1, 2, 3 });
Struktura generovaný typ ShimMyClass vypadá podobně jako následující kód:
// Fakes generated code
public class ShimMyClass : ShimBase<MyClass> {
public ShimMyClass Bind(IEnumerable<int> target) {
...
}
}
Změna výchozího chování
Každý typ generovaného překrytí obsahuje instanci IShimBehavior prostřednictvím rozhraní ShimBase<T>.InstanceBehavior vlastnost.Chování je použita vždy, když klient volá členem instance, která nebyla výslovně shimmed.
Pokud chování nebyla explicitně nastavena, bude používat instanci vrácenou statickou ShimsBehaviors.Current vlastnost.Standardně vrátí tato vlastnost chování, které vyvolá výjimku NotImplementedException.
Toto chování můžete kdykoliv změnit pomocí nastavení InstanceBehavior vlastnost na libovolnou instanci překrytí.Například následující úryvek změní překrytí na chování, které neprovádí žádnou akci nebo vrátí výchozí hodnotu návratový typ – to znamená, že default(T):
// unit test code
var shim = new ShimMyClass();
//return default(T) or do nothing
shim.InstanceBehavior = ShimsBehaviors.DefaultValue;
Chování lze také změnit globálně pro všechny instance shimmed u kterého InstanceBehavior nastavením statické nebyla explicitně nastavena vlastnost ShimsBehaviors.Current vlastnosti:
// unit test code
// change default shim for all shim instances
// where the behavior has not been set
ShimsBehaviors.Current =
ShimsBehaviors.DefaultValue;
Přistupuje k zjištění prostředí
Je možné připojit ke všem členům, včetně statických metod, přiřazením určitého typu chování ShimsBehaviors.NotImplemented chování do statické vlastnosti Behavior odpovídajícího typu překrytí:
// unit test code
// assigning the not implemented behavior
ShimMyClass.Behavior = ShimsBehaviors.NotImplemented;
// shorthand
ShimMyClass.BehaveAsNotImplemented();
Souběžnost
Překrytí typy platí pro všechny podprocesy v AppDomain a nemáte k dispozici příbuznosti podprocesu.To je důležité skutečnosti, pokud máte v úmyslu použít testu, které podporují souběžného zpracování: zkoušky týkající se typů překrytí nelze spustit současně.Tato vlastnost není enfored modulem runtime předstírá.
Volání metody původní z překrytí metody
Představte si, že jsme chtěli skutečně zapsat text do systému souborů, po ověření název souboru předaný metodě.V takovém případě by chceme zavolat metodu původní uprostřed metoda překrytí.
První přístup k vyřešení tohoto problému je zabalit volání původní metody pomocí delegáta a ShimsContext.ExecuteWithoutShims() jako v následujícím kódu:
// unit test code
ShimFile.WriteAllTextStringString = (fileName, content) => {
ShimsContext.ExecuteWithoutShims(() => {
Console.WriteLine("enter");
File.WriteAllText(fileName, content);
Console.WriteLine("leave");
});
};
Další možností je nastavit překrytí null, zavolejte metodu původní a obnovení překrytí.
// unit test code
ShimsDelegates.Action<string, string> shim = null;
shim = (fileName, content) => {
try {
Console.WriteLine("enter”);
// remove shim in order to call original method
ShimFile.WriteAllTextStringString = null;
File.WriteAllText(fileName, content);
}
finally
{
// restore shim
ShimFile.WriteAllTextStringString = shim;
Console.WriteLine("leave");
}
};
// initialize the shim
ShimFile.WriteAllTextStringString = shim;
Omezení
Překryvné ovladače nelze použít u všech typů z knihovny základní třídy rozhraní .NET mscorlib a systému.
Externí zdroje
Pokyny
Testování pro nepřetržité dodávky s Visual Studio 2012 – kapitola 2: testování: testování vnitřní
Viz také
Koncepty
Izolace testovaného kódu pomocí zástupného rozhraní Microsoft
Další zdroje
Peter Provost blogu: Visual Studio 2012 Překryvné ovladače
Video (1 h 16): testování Un-testable kódem předstírá v aplikaci Visual Studio 2012