Sdílet prostřednictvím


Vzájemná izolace částí aplikace pomocí zástupných procedur za účelem testování částí

Typy zástupných procedur jsou důležitou technologií poskytovanou architekturou Microsoft Fakes, která umožňuje snadnou izolaci komponenty, kterou testujete od ostatních komponent, na kterých spoléhá. Zástupný kód funguje jako malý kód, který během testování nahrazuje jinou komponentu. Klíčovou výhodou používání zástupných procedur je možnost získat konzistentní výsledky, aby bylo psaní testů jednodušší. I když ostatní komponenty ještě nejsou plně funkční, můžete testy stále spouštět pomocí zástupných procedur.

Pokud chcete efektivně použít zástupné procedury, doporučujeme navrhnout komponentu způsobem, který primárně závisí na rozhraních, nikoli na konkrétních třídách z jiných částí aplikace. Tento přístup návrhu podporuje oddělení a snižuje pravděpodobnost změn v jedné části, které vyžadují změny v jiné. Pokud jde o testování, tento vzor návrhu umožňuje nahradit implementaci zástupných procedur pro skutečnou komponentu, což usnadňuje efektivní izolaci a přesné testování cílové komponenty.

Podívejme se například na diagram, který znázorňuje zahrnuté komponenty:

Diagram of Real and Stub classes of StockAnalyzer.

V tomto diagramu je komponenta pod testem StockAnalyzer, která obvykle spoléhá na jinou komponentu volanou RealStockFeed. Představuje ale výzvu pro testování, protože pokaždé, když se volají metody, RealStockFeed vrací různé výsledky. Tato variabilita znesnadňuje zajištění konzistentního a spolehlivého StockAnalyzertestování .

Abychom tuto překážku během testování překonat, můžeme přijmout praxi injektáže závislostí. Tento přístup zahrnuje psaní kódu takovým způsobem, že explicitně nezmíní třídy v jiné komponentě vaší aplikace. Místo toho definujete rozhraní, které může druhá komponenta a zástupný kód implementovat pro testovací účely.

Tady je příklad použití injektáže závislostí v kódu:

public int GetContosoPrice(IStockFeed feed) => feed.GetSharePrice("COOO");

Omezení zástupných procedur

Projděte si následující omezení pro zástupné procedury.

Vytvoření zástupných procedur: Podrobný průvodce

Začněme tímto cvičením s motivačním příkladem: příkladem, který je znázorněný v předchozím diagramu.

Vytvoření knihovny tříd

Pokud chcete vytvořit knihovnu tříd, postupujte podle těchto kroků.

  1. Otevřete Visual Studio a vytvořte projekt knihovny tříd.

    Screenshot of Class Library project in Visual Studio.

  2. Konfigurace atributů projektu:

    • Nastavte název projektu na StockAnalysis.
    • Nastavte název řešení na StubsTutorial.
    • Nastavte cílovou architekturu projektu na .NET 8.0.
  3. Odstraňte výchozí soubor Class1.cs.

  4. Přidejte nový soubor S názvem IStockFeed.cs a zkopírujte do následující definice rozhraní:

    // IStockFeed.cs
    public interface IStockFeed
    {
        int GetSharePrice(string company);
    }
    
  5. Přidejte další nový soubor s názvem StockAnalyzer.cs a zkopírujte do následující definice třídy:

    // StockAnalyzer.cs
    public class StockAnalyzer
    {
        private IStockFeed stockFeed;
        public StockAnalyzer(IStockFeed feed)
        {
            stockFeed = feed;
        }
        public int GetContosoPrice()
        {
            return stockFeed.GetSharePrice("COOO");
        }
    }
    

Vytvoření testovacího projektu

Vytvořte testovací projekt pro cvičení.

  1. Klikněte pravým tlačítkem na řešení a přidejte nový projekt s názvem MSTest Test Project.

  2. Nastavte název projektu na TestProject.

  3. Nastavte cílovou architekturu projektu na .NET 8.0.

    Screenshot of Test project in Visual Studio.

Přidání sestavení Fakes

Přidejte sestavení Fakes pro projekt.

  1. Přidejte odkaz na projekt .StockAnalyzer

    Screenshot of the command Add Project Reference.

  2. Přidejte sestavení Fakes.

    1. V Průzkumník řešení vyhledejte odkaz na sestavení:

      • Pro starší projekt rozhraní .NET Framework (styl bez sady SDK) rozbalte uzel Reference projektu testů jednotek.

      • Pro projekt ve stylu sady SDK, který cílí na rozhraní .NET Framework, .NET Core nebo .NET 5.0 nebo novější, rozbalte uzel Závislosti a vyhledejte sestavení, které chcete zfalšovat v rámci sestavení, projektů nebo balíčků.

      • Pokud pracujete v jazyce Visual Basic, vyberte Zobrazit všechny soubory na panelu nástrojů Průzkumník řešení a zobrazte uzel Reference.

    2. Vyberte sestavení obsahující definice tříd, pro které chcete vytvořit zástupné procedury.

    3. V místní nabídce vyberte Přidat falešné sestavení.

      Screenshot of the command Add Fakes Assembly.

Vytvoření testu jednotek

Teď vytvořte test jednotek.

  1. Upravte výchozí soubor UnitTest1.cs a přidejte následující Test Method definici.

    [TestClass]
    class UnitTest1
    {
        [TestMethod]
        public void TestContosoPrice()
        {
            // Arrange:
            int priceToReturn = 345;
            string companyCodeUsed = "";
            var componentUnderTest = new StockAnalyzer(new StockAnalysis.Fakes.StubIStockFeed()
            {
                GetSharePriceString = (company) =>
                {
                    // Store the parameter value:
                    companyCodeUsed = company;
                    // Return the value prescribed by this test:
                    return priceToReturn;
                }
            });
    
            // Act:
            int actualResult = componentUnderTest.GetContosoPrice();
    
            // Assert:
            // Verify the correct result in the usual way:
            Assert.AreEqual(priceToReturn, actualResult);
    
            // Verify that the component made the correct call:
            Assert.AreEqual("COOO", companyCodeUsed);
        }
    }
    

    Zvláštní magie tady je StubIStockFeed třída. Pro každé rozhraní v odkazovaném sestavení generuje mechanismus rozhraní Microsoft Fakes zástupnou třídu. Název třídy zástupných procedur je odvozen od názvu rozhraní sFakes.Stub "" jako předpona a názvy typů parametrů připojené.

    Zástupné procedury jsou také generovány pro mechanismy získání a nastavení vlastností, pro události a pro obecné metody. Další informace najdete v tématu Použití zástupných procedur k izolaci částí aplikace od sebe pro účely testování jednotek.

    Screenshot of Solution Explorer showing all files.

  2. Otevřete Průzkumníka testů a spusťte test.

    Screenshot of Test Explorer.

Zástupné procedury pro různé druhy členů typu

Existují zástupné procedury pro různé druhy členů typu.

Metody

V uvedeném příkladu lze metody zakrýt připojením delegáta k instanci třídy zástupné procedury. Název typu zástupné procedury je odvozen z názvu metody a parametrů. Představte si například následující IStockFeed rozhraní a jeho metodu GetSharePrice:

// IStockFeed.cs
interface IStockFeed
{
    int GetSharePrice(string company);
}

Zástupný kód GetSharePrice připojíme pomocí:GetSharePriceString

// unit test code
var componentUnderTest = new StockAnalyzer(new StockAnalysis.Fakes.StubIStockFeed()
        {
            GetSharePriceString = (company) =>
            {
                // Store the parameter value:
                companyCodeUsed = company;
                // Return the value prescribed by this test:
                return priceToReturn;
            }
        });

Pokud pro metodu nezadáte zástupný znak, funkce Fakes vygeneruje funkci, která vrátí default value návratový typ. Pro čísla je výchozí hodnota 0. U typů tříd je null výchozí hodnota v jazyce C# nebo Nothing v jazyce Visual Basic.

Vlastnosti

Gettery a settery vlastností jsou vystaveny jako samostatné delegáty a mohou být potaženy jednotlivě. Představte si Value například vlastnost IStockFeedWithProperty:

interface IStockFeedWithProperty
{
    int Value { get; set; }
}

Chcete-li zaostřit na getter a setter a Value simulovat automatickou vlastnost, můžete použít následující kód:

// unit test code
int i = 5;
var stub = new StubIStockFeedWithProperty();
stub.ValueGet = () => i;
stub.ValueSet = (value) => i = value;

Pokud neposkytujete metody zástupných procedur pro setter nebo getter vlastnosti, Fakes generuje zástupný znak, který ukládá hodnoty, takže vlastnost zástupných procedur se chová jako jednoduchá proměnná.

Událost

Události se zveřejňují jako pole delegáta, což umožňuje, aby se jakákoli událost vyvolala jednoduše vyvoláním pole backingu události. Pojďme se podívat na následující rozhraní pro zástupný kód:

interface IStockFeedWithEvents
{
    event EventHandler Changed;
}

Pokud chcete vyvolat Changed událost, vyvoláte záložního delegáta:

// unit test code
var withEvents = new StubIStockFeedWithEvents();
// raising Changed
withEvents.ChangedEvent(withEvents, EventArgs.Empty);

Obecné metody

Obecné metody zástupných procedur můžete zadat delegátem pro každou požadovanou instanci metody. Například vzhledem k následujícímu rozhraní s obecnou metodou:

interface IGenericMethod
{
    T GetValue<T>();
}

Vytvoření instance můžete nasmát GetValue<int> následujícím způsobem:

[TestMethod]
public void TestGetValue()
{
    var stub = new StubIGenericMethod();
    stub.GetValueOf1<int>(() => 5);

    IGenericMethod target = stub;
    Assert.AreEqual(5, target.GetValue<int>());
}

Pokud kód volá GetValue<T> s jakoukoli jinou instancí, procedura chování provede.

Zástupné procedury virtuálních tříd

V předchozích příkladech byly zástupné procedury vytvořeny z rozhraní. Můžete však také generovat zástupné procedury z třídy, která má virtuální nebo abstraktní členy. Příklad:

// Base class in application under test
public abstract class MyClass
{
    public abstract void DoAbstract(string x);
    public virtual int DoVirtual(int n)
    {
        return n + 42;
    }

    public int DoConcrete()
    {
        return 1;
    }
}

V zástupných procedur vygenerovaných z této třídy můžete nastavit metody delegáta pro DoAbstract() , DoVirtual()ale ne DoConcrete().

// unit test
var stub = new Fakes.MyClass();
stub.DoAbstractString = (x) => { Assert.IsTrue(x>0); };
stub.DoVirtualInt32 = (n) => 10 ;

Pokud nezadáte delegáta pro virtuální metodu, fakes může buď poskytnout výchozí chování, nebo volat metodu v základní třídě. Chcete-li, aby byla volána základní metoda, nastavte CallBase vlastnost:

// unit test code
var stub = new Fakes.MyClass();
stub.CallBase = false;
// No delegate set - default delegate:
Assert.AreEqual(0, stub.DoVirtual(1));

stub.CallBase = true;
// No delegate set - calls the base:
Assert.AreEqual(43,stub.DoVirtual(1));

Změna výchozího chování zástupných procedur

Každý vygenerovaný typ zástupných procedur obsahuje instanci IStubBehavior rozhraní prostřednictvím IStub.InstanceBehavior vlastnosti. Toto chování se volá pokaždé, když klient zavolá člena bez připojeného vlastního delegáta. Pokud chování není nastaveno, použije instanci vrácenou StubsBehaviors.Current vlastností. Ve výchozím nastavení tato vlastnost vrací chování, které vyvolá NotImplementedException výjimku.

Chování můžete kdykoli změnit nastavením InstanceBehavior vlastnosti na libovolnou instanci zástupných procedur. Například následující fragment kódu změní chování tak, aby zástupný znak buď nic neudělal, nebo vrátil výchozí hodnotu návratového typu default(T):

// unit test code
var stub = new StockAnalysis.Fakes.StubIStockFeed();
// return default(T) or do nothing
stub.InstanceBehavior = StubsBehaviors.DefaultValue;

Toto chování lze také globálně změnit pro všechny objekty zástupných procedur, u kterých není chování nastaveno s StubsBehaviors.Current vlastností:

// Change default behavior for all stub instances where the behavior has not been set.
StubBehaviors.Current = BehavedBehaviors.DefaultValue;