Sdílet prostřednictvím


Správa hostitele aplikace v testech .NET.NET Aspire

Při psaní funkčních nebo integračních testů pomocí .NET.NET Aspireje efektivní správa instance hostitele aplikace klíčová. Hostitel aplikace představuje celé aplikační prostředí a může být nákladné vytvářet a rušit. Tento článek vysvětluje, jak spravovat instanci hostující aplikace v .NETa.NET Aspire testech.

Pro psaní testů pomocí .NET.NET Aspirepoužijete balíček 📦 Aspire.Hosting.Testing NuGet, který obsahuje některé pomocné třídy pro správu instance hostitele aplikace v testech.

Použití třídy DistributedApplicationTestingBuilder

V kurzu psaní prvního testujste se seznámili s DistributedApplicationTestingBuilder třídou, která se dá použít k vytvoření hostující instance aplikace.

var appHost = await DistributedApplicationTestingBuilder
    .CreateAsync<Projects.AspireApp_AppHost>();

Metoda DistributedApplicationTestingBuilder.CreateAsync<T> vezme typ projektu hostitele aplikace jako obecný parametr pro vytvoření instance hostitele aplikace. I když se tato metoda provádí na začátku každého testu, je efektivnější vytvořit instanci hostitele aplikace jednou a sdílet ji napříč testy s rostoucí sadou testů.

S xUnit implementujete rozhraní IAsyncLifetime na testovací třídě pro podporu asynchronní inicializace a odstranění instance hostitele aplikace. Metoda InitializeAsync slouží k vytvoření instance hostitele aplikace před spuštěním testů a DisposeAsync metoda odstraní hostitele aplikace po dokončení testů.

public class WebTests : IAsyncLifetime
{
    private DistributedApplication _app;

    public async Task InitializeAsync()
    {
        var appHost = await DistributedApplicationTestingBuilder
            .CreateAsync<Projects.AspireApp_AppHost>();

        _app = await appHost.BuildAsync();
    }

    public async Task DisposeAsync() => await _app.DisposeAsync();

    [Fact]
    public async Task GetWebResourceRootReturnsOkStatusCode()
    {
        // test code here
    }
}

Pomocí MSTest použijete ClassInitializeAttribute a ClassCleanupAttribute na statických metodách testovací třídy k zajištění inicializace a vyčištění instance hostitele aplikace. Metoda ClassInitialize slouží k vytvoření instance hostitele aplikace před spuštěním testů a ClassCleanup metoda odstraní instanci hostitele aplikace po dokončení testů.

[TestClass]
public class WebTests
{
    private static DistributedApplication _app;

    [ClassInitialize]
    public static async Task ClassInitialize(TestContext context)
    {
       var appHost = await DistributedApplicationTestingBuilder
            .CreateAsync<Projects.AspireApp_AppHost>();

        _app = await appHost.BuildAsync();
    }

    [ClassCleanup]
    public static async Task ClassCleanup() => await _app.DisposeAsync();

    [TestMethod]
    public async Task GetWebResourceRootReturnsOkStatusCode()
    {
        // test code here
    }
}

Pomocí NUnit použijete OneTimeSetUp a OneTimeTearDown atributy na metodách testovací třídy k zajištění nastavení a odbourání instance hostitele aplikace. Metoda OneTimeSetUp slouží k vytvoření instance hostitele aplikace před spuštěním testů a OneTimeTearDown metoda odstraní instanci hostitele aplikace po dokončení testů.

public class WebTests
{
    private DistributedApplication _app;

    [OneTimeSetUp]
    public async Task OneTimeSetup()
    {
       var appHost = await DistributedApplicationTestingBuilder
            .CreateAsync<Projects.AspireApp_AppHost>();

        _app = await appHost.BuildAsync();
    }

    [OneTimeTearDown]
    public async Task OneTimeTearDown() => await _app.DisposeAsync();

    [Test]
    public async Task GetWebResourceRootReturnsOkStatusCode()
    {
        // test code here
    }
}

Zachycením hostitele aplikace v poli při spuštění testu můžete přistupovat k němu v každém testu, aniž byste ho museli znovu vytvořit, což snižuje dobu potřebnou pro jejich provedení. Po dokončení testovacího běhu je hostitel aplikace uvolněn, což uklidí všechny prostředky vytvořené během testování, jako jsou kontejnery.

Předání argumentů hostiteli aplikace

K argumentům můžete přistupovat z hostitele aplikace pomocí parametru args. Argumenty se také předávají .NETkonfiguračnímu systému, takže můžete tímto způsobem přepsat mnoho nastavení konfigurace. V následujícím příkladu přepíšete prostředí zadáním možnosti příkazového řádku:

var builder = await DistributedApplicationTestingBuilder
    .CreateAsync<Projects.MyAppHost>(
    [
        "--environment=Testing"
    ]);

Další argumenty je možné předat hostiteli aplikace Program a zpřístupnit je v hostiteli aplikace. V dalším příkladu předáte hostiteli aplikace argument a použijete ho k řízení, jestli do instance Postgres přidáte datové svazky.

V hostiteli aplikace Programpoužijete konfiguraci, která umožňuje povolení nebo zakázání svazků:

var postgres = builder.AddPostgres("postgres1");
if (builder.Configuration.GetValue("UseVolumes", true))
{
    postgres.WithDataVolume();
}

V testovacím kódu předáte "UseVolumes=false" v args hostiteli aplikace:

public async Task DisableVolumesFromTest()
{
    // Disable volumes in the test builder via arguments:
    using var builder = await DistributedApplicationTestingBuilder
        .CreateAsync<Projects.TestingAppHost1_AppHost>(
        [
            "UseVolumes=false"
        ]);

    // The container will have no volume annotation since we disabled volumes by passing UseVolumes=false
    var postgres = builder.Resources.Single(r => r.Name == "postgres1");

    Assert.Empty(postgres.Annotations.OfType<ContainerMountAnnotation>());
}

Použití třídy DistributedApplicationFactory

I když je třída DistributedApplicationTestingBuilder užitečná pro mnoho scénářů, můžou se zde vyskytovat situace, kdy chcete mít větší kontrolu nad spuštěním hostitele aplikace, například spuštěním kódu před vytvořením tvůrce nebo po sestavení hostitele aplikace. V těchto případech implementujete vlastní verzi třídy DistributedApplicationFactory. To je to, co DistributedApplicationTestingBuilder interně používá.

public class TestingAspireAppHost()
    : DistributedApplicationFactory(typeof(Projects.AspireApp_AppHost))
{
    // override methods here
}

Konstruktor vyžaduje typ odkazu na projekt hostitele aplikace jako parametr. Volitelně můžete zadat argumenty podkladovému tvůrci hostitelských aplikací. Tyto argumenty určují, jak se hostitel aplikace spouští, a poskytují hodnoty proměnné args používané Program.cs souborem ke spuštění instance hostitele aplikace.

Metody životního cyklu

Třída DistributionApplicationFactory poskytuje několik metod životního cyklu, které je možné přepsat, aby poskytovaly vlastní chování během přípravy a vytváření hostitele aplikace. Dostupné metody jsou OnBuilderCreating, OnBuilderCreated, OnBuildinga OnBuilt.

Můžeme například použít metodu OnBuilderCreating k nastavení konfigurace, jako jsou například informace o předplatném a skupině prostředků pro Azure, před vytvořením hostitele aplikace a zřízením všech závislých Azure prostředků, což vede k tomu, že naše testy používají správné Azure prostředí.

public class TestingAspireAppHost() : DistributedApplicationFactory(typeof(Projects.AspireApp_AppHost))
{
    protected override void OnBuilderCreating(DistributedApplicationOptions applicationOptions, HostApplicationBuilderSettings hostOptions)
    {
        hostOptions.Configuration ??= new();
        hostOptions.Configuration["environment"] = "Development";
        hostOptions.Configuration["AZURE_SUBSCRIPTION_ID"] = "00000000-0000-0000-0000-000000000000";
        hostOptions.Configuration["AZURE_RESOURCE_GROUP"] = "my-resource-group";
    }
}

Vzhledem k pořadí precedence v konfiguračním systému .NET se proměnné prostředí použijí před vším v souboru appsettings.json nebo secrets.json.

Dalším scénářem, který můžete chtít použít v životním cyklu, je konfigurace služeb používaných hostitelem aplikace. V následujícím příkladu zvažte scénář, ve kterém překročíte API OnBuilderCreated, abyste přidali odolnost k HttpClient:

protected override void OnBuilderCreated(
    DistributedApplicationBuilder applicationBuilder)
{
    applicationBuilder.Services.ConfigureHttpClientDefaults(clientBuilder =>
    {
        clientBuilder.AddStandardResilienceHandler();
    });
}

Viz také