Sdílet prostřednictvím


Razor Testy jednotek stránek v ASP.NET Core

ASP.NET Core podporuje testy Razor jednotek aplikací Pages. Testy vrstvy přístupu k datům (DAL) a stránkových modelů pomáhají zajistit:

  • Razor Části aplikace Pages fungují nezávisle a společně jako jednotka při vytváření aplikace.
  • Třídy a metody mají omezené rozsahy odpovědnosti.
  • Existuje další dokumentace k chování aplikace.
  • Během automatizovaného sestavování a nasazování se nacházejí regrese, což jsou chyby způsobené aktualizacemi kódu.

V tomto tématu se předpokládá, že máte základní znalosti o aplikacích Razor Pages a testech jednotek. Pokud neznáte Razor aplikace Pages nebo testovací koncepty, projděte si následující témata:

Zobrazení nebo stažení ukázkového kódu (postup stažení)

Ukázkový projekt se skládá ze dvou aplikací:

Aplikace Složka projektu Popis
Aplikace zpráv src/RazorPagesTestSample Umožňuje uživateli přidat zprávu, odstranit jednu zprávu, odstranit všechny zprávy a analyzovat zprávy (najít průměrný počet slov na zprávu).
Testovací aplikace tests/RazorPagesTestSample.Tests Používá se k testování modelu stránky DAL a Index aplikace zpráv.

Testy je možné spouštět pomocí integrovaných testovacích funkcí integrovaného vývojového prostředí (IDE), například sady Visual Studio. Pokud používáte Visual Studio Code nebo příkazový řádek, spusťte na příkazovém řádku ve složce tests/RazorPagesTestSample.Tests následující příkaz:

dotnet test

Organizace aplikace zpráv

Aplikace zpráv je Razor systém zpráv Pages s následujícími vlastnostmi:

  • Indexová stránka aplikace (Pages/Index.cshtml a Pages/Index.cshtml.cs) poskytuje metody uživatelského rozhraní a modelu stránek pro řízení sčítání, odstranění a analýzy zpráv (zjištění průměrného počtu slov na zprávu).
  • Zpráva je popsána Message třídou (Data/Message.cs) se dvěma vlastnostmi: Id (klíč) a Text (zpráva). Vlastnost Text je povinná a omezená na 200 znaků.
  • Zprávy se ukládají pomocí databáze v paměti entity Framework†.
  • Aplikace obsahuje DAL ve své třídě AppDbContext kontextu databáze (Data/AppDbContext.cs). Metody DAL jsou označeny virtual, což umožňuje napodobování metod pro použití v testech.
  • Pokud je databáze při spuštění aplikace prázdná, úložiště zpráv se inicializuje se třemi zprávami. Tyto počáteční zprávy se používají také v testech.

†Pokudování pomocí nástroje InMemory vysvětluje, jak používat databázi v paměti pro testy s msTestem. Toto téma používá testovací architekturu xUnit . Koncepty testů a implementace testů v různých testovacích architekturách jsou podobné, ale ne identické.

I když ukázková aplikace nepoužívá vzor úložiště a není efektivním příkladem modeluRazor Práce (UoW), stránky tyto vzory vývoje podporují. Další informace najdete v tématu Návrh vrstvy trvalosti infrastruktury a logiky kontroleru testů v ASP.NET Core (ukázka implementuje vzor úložiště).

Testovací organizace aplikací

Testovací aplikace je konzolová aplikace uvnitř složky tests/RazorPagesTestSample.Tests .

Testovací složka aplikace Popis
UnitTests
  • DataAccessLayerTest.cs obsahuje testy jednotek pro DAL.
  • IndexPageTests.cs obsahuje testy jednotek pro model indexové stránky.
Nástroje Obsahuje metodu použitou TestDbContextOptions k vytvoření nových možností kontextu databáze pro každý test jednotek DAL, aby se databáze resetovala na základní podmínku pro každý test.

Testovací architektura je xUnit. Architektura napodobování objektů je Moq.

Testy jednotek vrstvy přístupu k datům (DAL)

Aplikace zpráv má DAL se čtyřmi metodami obsaženými AppDbContext ve třídě (src/RazorPagesTestSample/Data/AppDbContext.cs). Každá metoda má v testovací aplikaci jeden nebo dva testy jednotek.

Metoda DAL Function
GetMessagesAsync List<Message> Získá z databáze seřazené podle Text vlastnosti.
AddMessageAsync Message Přidá do databáze.
DeleteAllMessagesAsync Odstraní všechny Message položky z databáze.
DeleteMessageAsync Odstraní jeden z Message databáze .Id

Testy jednotek dal vyžadují DbContextOptions při vytváření nového AppDbContext pro každý test. Jedním z přístupů k vytvoření DbContextOptions každého testu je použití DbContextOptionsBuilder:

var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>()
    .UseInMemoryDatabase("InMemoryDb");

using (var db = new AppDbContext(optionsBuilder.Options))
{
    // Use the db here in the unit test.
}

Problém s tímto přístupem spočívá v tom, že každý test obdrží databázi v libovolném stavu, který předchozí test opustil. To může být problematické při pokusu o zápis atomických testů jednotek, které vzájemně neruší. Pokud chcete vynutit AppDbContext použití nového kontextu databáze pro každý test, zadejte DbContextOptions instanci založenou na novém poskytovateli služeb. Testovací aplikace ukazuje, jak to provést pomocí metody TestDbContextOptions třídy Utilities (tests/RazorPagesTestSample.Tests/Utilities/Utilities.cs):

public static DbContextOptions<AppDbContext> TestDbContextOptions()
{
    // Create a new service provider to create a new in-memory database.
    var serviceProvider = new ServiceCollection()
        .AddEntityFrameworkInMemoryDatabase()
        .BuildServiceProvider();

    // Create a new options instance using an in-memory database and 
    // IServiceProvider that the context should resolve all of its 
    // services from.
    var builder = new DbContextOptionsBuilder<AppDbContext>()
        .UseInMemoryDatabase("InMemoryDb")
        .UseInternalServiceProvider(serviceProvider);

    return builder.Options;
}

DbContextOptions Použití testů jednotek DAL umožňuje každému testu spustit atomicky s novou instancí databáze:

using (var db = new AppDbContext(Utilities.TestDbContextOptions()))
{
    // Use the db here in the unit test.
}

Každá testovací metoda ve DataAccessLayerTest třídě (UnitTests/DataAccessLayerTest.cs) se řídí podobným vzorem Arrange-Act-Assert:

  1. Uspořádání: Databáze je nakonfigurovaná pro test a/nebo je definován očekávaný výsledek.
  2. Act: Test je proveden.
  3. Assert: Kontrolní výrazy jsou provedeny k určení, zda je výsledek testu úspěšný.

Metoda je například DeleteMessageAsync zodpovědná za odebrání jedné zprávy identifikované jejím Id (src/RazorPagesTestSample/Data/AppDbContext.cs):

public async virtual Task DeleteMessageAsync(int id)
{
    var message = await Messages.FindAsync(id);

    if (message != null)
    {
        Messages.Remove(message);
        await SaveChangesAsync();
    }
}

Pro tuto metodu existují dva testy. Jeden test zkontroluje, že metoda odstraní zprávu, když je zpráva přítomna v databázi. Druhá metoda testuje, že se databáze nezmění, pokud zpráva Id pro odstranění neexistuje. Metoda DeleteMessageAsync_MessageIsDeleted_WhenMessageIsFound je zobrazena níže:

[Fact]
public async Task DeleteMessageAsync_MessageIsDeleted_WhenMessageIsFound()
{
    using (var db = new AppDbContext(Utilities.TestDbContextOptions()))
    {
        // Arrange
        var seedMessages = AppDbContext.GetSeedingMessages();
        await db.AddRangeAsync(seedMessages);
        await db.SaveChangesAsync();
        var recId = 1;
        var expectedMessages = 
            seedMessages.Where(message => message.Id != recId).ToList();

        // Act
        await db.DeleteMessageAsync(recId);

        // Assert
        var actualMessages = await db.Messages.AsNoTracking().ToListAsync();
        Assert.Equal(
            expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), 
            actualMessages.OrderBy(m => m.Id).Select(m => m.Text));
    }
}

Nejprve metoda provede krok Uspořádat, kde probíhá příprava na krok Act. Počáteční zprávy jsou získány a uloženy v seedMessages. Počáteční zprávy se ukládají do databáze. Zpráva s parametrem of Id 1 je nastavena pro odstranění. DeleteMessageAsync Při spuštění metody by očekávané zprávy měly mít všechny zprávy s výjimkou těch, které obsahují .Id 1 Proměnná expectedMessages představuje tento očekávaný výsledek.

// Arrange
var seedMessages = AppDbContext.GetSeedingMessages();
await db.AddRangeAsync(seedMessages);
await db.SaveChangesAsync();
var recId = 1;
var expectedMessages = 
    seedMessages.Where(message => message.Id != recId).ToList();

Metoda funguje: Metoda DeleteMessageAsync se provádí předáváním recId 1:

// Act
await db.DeleteMessageAsync(recId);

Nakonec metoda získá Messages z kontextu a porovná ji s expectedMessages tvrzením, že jsou obě stejné:

// Assert
var actualMessages = await db.Messages.AsNoTracking().ToListAsync();
Assert.Equal(
    expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), 
    actualMessages.OrderBy(m => m.Id).Select(m => m.Text));

Chcete-li porovnat, že oba List<Message> jsou stejné:

  • Zprávy jsou seřazeny podle Id.
  • Dvojice zpráv se porovnávají s Text vlastností.

Podobná testovací metoda DeleteMessageAsync_NoMessageIsDeleted_WhenMessageIsNotFound zkontroluje výsledek pokusu o odstranění zprávy, která neexistuje. V tomto případě by se očekávané zprávy v databázi měly po spuštění metody rovnat skutečným zprávům DeleteMessageAsync . Obsah databáze by neměl být změněn:

[Fact]
public async Task DeleteMessageAsync_NoMessageIsDeleted_WhenMessageIsNotFound()
{
    using (var db = new AppDbContext(Utilities.TestDbContextOptions()))
    {
        // Arrange
        var expectedMessages = AppDbContext.GetSeedingMessages();
        await db.AddRangeAsync(expectedMessages);
        await db.SaveChangesAsync();
        var recId = 4;

        // Act
        try
        {
            await db.DeleteMessageAsync(recId);
        }
        catch
        {
            // recId doesn't exist
        }

        // Assert
        var actualMessages = await db.Messages.AsNoTracking().ToListAsync();
        Assert.Equal(
            expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), 
            actualMessages.OrderBy(m => m.Id).Select(m => m.Text));
    }
}

Testy jednotek metod modelu stránky

Další sada testů jednotek zodpovídá za testy metod modelu stránky. V aplikaci zpráv jsou modely stránky indexu nalezeny IndexModel ve třídě v src/RazorPagesTestSample/Pages/Index.cshtml.cs.

Metoda stránkování modelu Function
OnGetAsync Získá zprávy z DAL pro uživatelské rozhraní pomocí GetMessagesAsync metody.
OnPostAddMessageAsync Pokud je ModelState platný, volání AddMessageAsync pro přidání zprávy do databáze.
OnPostDeleteAllMessagesAsync Volání DeleteAllMessagesAsync pro odstranění všech zpráv v databázi
OnPostDeleteMessageAsync DeleteMessageAsync Provede odstranění zprávy se zadaným parametremId.
OnPostAnalyzeMessagesAsync Pokud je jedna nebo více zpráv v databázi, vypočítá průměrný počet slov na zprávu.

Metody stránkového modelu se testují pomocí sedmi testů ve IndexPageTests třídě (tests/RazorPagesTestSample.Tests/UnitTests/IndexPageTests.cs). Testy používají známý vzor Uspořádat-Assert-Act. Tyto testy se zaměřují na:

  • Určení, zda metody dodržují správné chování v případě, že ModelState je neplatný.
  • Potvrzení metody vytvoří správnou IActionResult.
  • Zkontrolujte správnost přiřazení hodnot vlastností.

Tato skupinatestůch Například GetMessagesAsync metoda je AppDbContext napodobena k vytvoření výstupu. Když metoda stránkového modelu spustí tuto metodu, vrátí napodobení výsledek. Data nepocházejí z databáze. Tím se vytvoří předvídatelné a spolehlivé testovací podmínky pro použití DAL v testech modelu stránky.

Test OnGetAsync_PopulatesThePageModel_WithAListOfMessages ukazuje, jak GetMessagesAsync je metoda napodobena pro model stránky:

var mockAppDbContext = new Mock<AppDbContext>(optionsBuilder.Options);
var expectedMessages = AppDbContext.GetSeedingMessages();
mockAppDbContext.Setup(
    db => db.GetMessagesAsync()).Returns(Task.FromResult(expectedMessages));
var pageModel = new IndexModel(mockAppDbContext.Object);

OnGetAsync Když se metoda spustí v kroku Act, volá metodu modelu GetMessagesAsync stránky.

Krok zákona o testování jednotek (tests/RazorPagesTestSample.Tests/UnitTests/IndexPageTests.cs):

// Act
await pageModel.OnGetAsync();

IndexPage metoda modelu OnGetAsync stránky (src/RazorPagesTestSample/Pages/Index.cshtml.cs):

public async Task OnGetAsync()
{
    Messages = await _db.GetMessagesAsync();
}

Metoda GetMessagesAsync v dal nevrací výsledek pro volání této metody. Napodobená verze metody vrátí výsledek.

Assert V kroku jsou skutečné zprávy (actualMessages) přiřazeny z Messages vlastnosti modelu stránky. Při přiřazení zpráv se také provede kontrola typu. Očekávané a skutečné zprávy se porovnávají podle jejich Text vlastností. Test tvrdí, že dvě List<Message> instance obsahují stejné zprávy.

// Assert
var actualMessages = Assert.IsAssignableFrom<List<Message>>(pageModel.Messages);
Assert.Equal(
    expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), 
    actualMessages.OrderBy(m => m.Id).Select(m => m.Text));

Další testy v této skupině vytvářejí objekty modelu stránky, které zahrnují DefaultHttpContextobjekty , ModelStateDictionary, ViewDataDictionaryActionContext PageContexta , a .PageContext To je užitečné při provádění testů. Aplikace zpráv například naváže ModelState chybu s AddModelError cílem zkontrolovat, jestli je při spuštění vrácena OnPostAddMessageAsync platná PageResult hodnota:

[Fact]
public async Task OnPostAddMessageAsync_ReturnsAPageResult_WhenModelStateIsInvalid()
{
    // Arrange
    var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>()
        .UseInMemoryDatabase("InMemoryDb");
    var mockAppDbContext = new Mock<AppDbContext>(optionsBuilder.Options);
    var expectedMessages = AppDbContext.GetSeedingMessages();
    mockAppDbContext.Setup(db => db.GetMessagesAsync()).Returns(Task.FromResult(expectedMessages));
    var httpContext = new DefaultHttpContext();
    var modelState = new ModelStateDictionary();
    var actionContext = new ActionContext(httpContext, new RouteData(), new PageActionDescriptor(), modelState);
    var modelMetadataProvider = new EmptyModelMetadataProvider();
    var viewData = new ViewDataDictionary(modelMetadataProvider, modelState);
    var tempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>());
    var pageContext = new PageContext(actionContext)
    {
        ViewData = viewData
    };
    var pageModel = new IndexModel(mockAppDbContext.Object)
    {
        PageContext = pageContext,
        TempData = tempData,
        Url = new UrlHelper(actionContext)
    };
    pageModel.ModelState.AddModelError("Message.Text", "The Text field is required.");

    // Act
    var result = await pageModel.OnPostAddMessageAsync();

    // Assert
    Assert.IsType<PageResult>(result);
}

Další materiály

ASP.NET Core podporuje testy Razor jednotek aplikací Pages. Testy vrstvy přístupu k datům (DAL) a stránkových modelů pomáhají zajistit:

  • Razor Části aplikace Pages fungují nezávisle a společně jako jednotka při vytváření aplikace.
  • Třídy a metody mají omezené rozsahy odpovědnosti.
  • Existuje další dokumentace k chování aplikace.
  • Během automatizovaného sestavování a nasazování se nacházejí regrese, což jsou chyby způsobené aktualizacemi kódu.

V tomto tématu se předpokládá, že máte základní znalosti o aplikacích Razor Pages a testech jednotek. Pokud neznáte Razor aplikace Pages nebo testovací koncepty, projděte si následující témata:

Zobrazení nebo stažení ukázkového kódu (postup stažení)

Ukázkový projekt se skládá ze dvou aplikací:

Aplikace Složka projektu Popis
Aplikace zpráv src/RazorPagesTestSample Umožňuje uživateli přidat zprávu, odstranit jednu zprávu, odstranit všechny zprávy a analyzovat zprávy (najít průměrný počet slov na zprávu).
Testovací aplikace tests/RazorPagesTestSample.Tests Používá se k testování modelu stránky DAL a Index aplikace zpráv.

Testy je možné spouštět pomocí integrovaných testovacích funkcí integrovaného vývojového prostředí (IDE), například sady Visual Studio. Pokud používáte Visual Studio Code nebo příkazový řádek, spusťte na příkazovém řádku ve složce tests/RazorPagesTestSample.Tests následující příkaz:

dotnet test

Organizace aplikace zpráv

Aplikace zpráv je Razor systém zpráv Pages s následujícími vlastnostmi:

  • Indexová stránka aplikace (Pages/Index.cshtml a Pages/Index.cshtml.cs) poskytuje metody uživatelského rozhraní a modelu stránek pro řízení sčítání, odstranění a analýzy zpráv (zjištění průměrného počtu slov na zprávu).
  • Zpráva je popsána Message třídou (Data/Message.cs) se dvěma vlastnostmi: Id (klíč) a Text (zpráva). Vlastnost Text je povinná a omezená na 200 znaků.
  • Zprávy se ukládají pomocí databáze v paměti entity Framework†.
  • Aplikace obsahuje DAL ve své třídě AppDbContext kontextu databáze (Data/AppDbContext.cs). Metody DAL jsou označeny virtual, což umožňuje napodobování metod pro použití v testech.
  • Pokud je databáze při spuštění aplikace prázdná, úložiště zpráv se inicializuje se třemi zprávami. Tyto počáteční zprávy se používají také v testech.

†Pokudování pomocí nástroje InMemory vysvětluje, jak používat databázi v paměti pro testy s msTestem. Toto téma používá testovací architekturu xUnit . Koncepty testů a implementace testů v různých testovacích architekturách jsou podobné, ale ne identické.

I když ukázková aplikace nepoužívá vzor úložiště a není efektivním příkladem modeluRazor Práce (UoW), stránky tyto vzory vývoje podporují. Další informace najdete v tématu Návrh vrstvy trvalosti infrastruktury a logiky kontroleru testů v ASP.NET Core (ukázka implementuje vzor úložiště).

Testovací organizace aplikací

Testovací aplikace je konzolová aplikace uvnitř složky tests/RazorPagesTestSample.Tests .

Testovací složka aplikace Popis
UnitTests
  • DataAccessLayerTest.cs obsahuje testy jednotek pro DAL.
  • IndexPageTests.cs obsahuje testy jednotek pro model indexové stránky.
Nástroje Obsahuje metodu použitou TestDbContextOptions k vytvoření nových možností kontextu databáze pro každý test jednotek DAL, aby se databáze resetovala na základní podmínku pro každý test.

Testovací architektura je xUnit. Architektura napodobování objektů je Moq.

Testy jednotek vrstvy přístupu k datům (DAL)

Aplikace zpráv má DAL se čtyřmi metodami obsaženými AppDbContext ve třídě (src/RazorPagesTestSample/Data/AppDbContext.cs). Každá metoda má v testovací aplikaci jeden nebo dva testy jednotek.

Metoda DAL Function
GetMessagesAsync List<Message> Získá z databáze seřazené podle Text vlastnosti.
AddMessageAsync Message Přidá do databáze.
DeleteAllMessagesAsync Odstraní všechny Message položky z databáze.
DeleteMessageAsync Odstraní jeden z Message databáze .Id

Testy jednotek dal vyžadují DbContextOptions při vytváření nového AppDbContext pro každý test. Jedním z přístupů k vytvoření DbContextOptions každého testu je použití DbContextOptionsBuilder:

var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>()
    .UseInMemoryDatabase("InMemoryDb");

using (var db = new AppDbContext(optionsBuilder.Options))
{
    // Use the db here in the unit test.
}

Problém s tímto přístupem spočívá v tom, že každý test obdrží databázi v libovolném stavu, který předchozí test opustil. To může být problematické při pokusu o zápis atomických testů jednotek, které vzájemně neruší. Pokud chcete vynutit AppDbContext použití nového kontextu databáze pro každý test, zadejte DbContextOptions instanci založenou na novém poskytovateli služeb. Testovací aplikace ukazuje, jak to provést pomocí metody TestDbContextOptions třídy Utilities (tests/RazorPagesTestSample.Tests/Utilities/Utilities.cs):

public static DbContextOptions<AppDbContext> TestDbContextOptions()
{
    // Create a new service provider to create a new in-memory database.
    var serviceProvider = new ServiceCollection()
        .AddEntityFrameworkInMemoryDatabase()
        .BuildServiceProvider();

    // Create a new options instance using an in-memory database and 
    // IServiceProvider that the context should resolve all of its 
    // services from.
    var builder = new DbContextOptionsBuilder<AppDbContext>()
        .UseInMemoryDatabase("InMemoryDb")
        .UseInternalServiceProvider(serviceProvider);

    return builder.Options;
}

DbContextOptions Použití testů jednotek DAL umožňuje každému testu spustit atomicky s novou instancí databáze:

using (var db = new AppDbContext(Utilities.TestDbContextOptions()))
{
    // Use the db here in the unit test.
}

Každá testovací metoda ve DataAccessLayerTest třídě (UnitTests/DataAccessLayerTest.cs) se řídí podobným vzorem Arrange-Act-Assert:

  1. Uspořádání: Databáze je nakonfigurovaná pro test a/nebo je definován očekávaný výsledek.
  2. Act: Test je proveden.
  3. Assert: Kontrolní výrazy jsou provedeny k určení, zda je výsledek testu úspěšný.

Metoda je například DeleteMessageAsync zodpovědná za odebrání jedné zprávy identifikované jejím Id (src/RazorPagesTestSample/Data/AppDbContext.cs):

public async virtual Task DeleteMessageAsync(int id)
{
    var message = await Messages.FindAsync(id);

    if (message != null)
    {
        Messages.Remove(message);
        await SaveChangesAsync();
    }
}

Pro tuto metodu existují dva testy. Jeden test zkontroluje, že metoda odstraní zprávu, když je zpráva přítomna v databázi. Druhá metoda testuje, že se databáze nezmění, pokud zpráva Id pro odstranění neexistuje. Metoda DeleteMessageAsync_MessageIsDeleted_WhenMessageIsFound je zobrazena níže:

[Fact]
public async Task DeleteMessageAsync_MessageIsDeleted_WhenMessageIsFound()
{
    using (var db = new AppDbContext(Utilities.TestDbContextOptions()))
    {
        // Arrange
        var seedMessages = AppDbContext.GetSeedingMessages();
        await db.AddRangeAsync(seedMessages);
        await db.SaveChangesAsync();
        var recId = 1;
        var expectedMessages = 
            seedMessages.Where(message => message.Id != recId).ToList();

        // Act
        await db.DeleteMessageAsync(recId);

        // Assert
        var actualMessages = await db.Messages.AsNoTracking().ToListAsync();
        Assert.Equal(
            expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), 
            actualMessages.OrderBy(m => m.Id).Select(m => m.Text));
    }
}

Nejprve metoda provede krok Uspořádat, kde probíhá příprava na krok Act. Počáteční zprávy jsou získány a uloženy v seedMessages. Počáteční zprávy se ukládají do databáze. Zpráva s parametrem of Id 1 je nastavena pro odstranění. DeleteMessageAsync Při spuštění metody by očekávané zprávy měly mít všechny zprávy s výjimkou těch, které obsahují .Id 1 Proměnná expectedMessages představuje tento očekávaný výsledek.

// Arrange
var seedMessages = AppDbContext.GetSeedingMessages();
await db.AddRangeAsync(seedMessages);
await db.SaveChangesAsync();
var recId = 1;
var expectedMessages = 
    seedMessages.Where(message => message.Id != recId).ToList();

Metoda funguje: Metoda DeleteMessageAsync se provádí předáváním recId 1:

// Act
await db.DeleteMessageAsync(recId);

Nakonec metoda získá Messages z kontextu a porovná ji s expectedMessages tvrzením, že jsou obě stejné:

// Assert
var actualMessages = await db.Messages.AsNoTracking().ToListAsync();
Assert.Equal(
    expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), 
    actualMessages.OrderBy(m => m.Id).Select(m => m.Text));

Chcete-li porovnat, že oba List<Message> jsou stejné:

  • Zprávy jsou seřazeny podle Id.
  • Dvojice zpráv se porovnávají s Text vlastností.

Podobná testovací metoda DeleteMessageAsync_NoMessageIsDeleted_WhenMessageIsNotFound zkontroluje výsledek pokusu o odstranění zprávy, která neexistuje. V tomto případě by se očekávané zprávy v databázi měly po spuštění metody rovnat skutečným zprávům DeleteMessageAsync . Obsah databáze by neměl být změněn:

[Fact]
public async Task DeleteMessageAsync_NoMessageIsDeleted_WhenMessageIsNotFound()
{
    using (var db = new AppDbContext(Utilities.TestDbContextOptions()))
    {
        // Arrange
        var expectedMessages = AppDbContext.GetSeedingMessages();
        await db.AddRangeAsync(expectedMessages);
        await db.SaveChangesAsync();
        var recId = 4;

        // Act
        await db.DeleteMessageAsync(recId);

        // Assert
        var actualMessages = await db.Messages.AsNoTracking().ToListAsync();
        Assert.Equal(
            expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), 
            actualMessages.OrderBy(m => m.Id).Select(m => m.Text));
    }
}

Testy jednotek metod modelu stránky

Další sada testů jednotek zodpovídá za testy metod modelu stránky. V aplikaci zpráv jsou modely stránky indexu nalezeny IndexModel ve třídě v src/RazorPagesTestSample/Pages/Index.cshtml.cs.

Metoda stránkování modelu Function
OnGetAsync Získá zprávy z DAL pro uživatelské rozhraní pomocí GetMessagesAsync metody.
OnPostAddMessageAsync Pokud je ModelState platný, volání AddMessageAsync pro přidání zprávy do databáze.
OnPostDeleteAllMessagesAsync Volání DeleteAllMessagesAsync pro odstranění všech zpráv v databázi
OnPostDeleteMessageAsync DeleteMessageAsync Provede odstranění zprávy se zadaným parametremId.
OnPostAnalyzeMessagesAsync Pokud je jedna nebo více zpráv v databázi, vypočítá průměrný počet slov na zprávu.

Metody stránkového modelu se testují pomocí sedmi testů ve IndexPageTests třídě (tests/RazorPagesTestSample.Tests/UnitTests/IndexPageTests.cs). Testy používají známý vzor Uspořádat-Assert-Act. Tyto testy se zaměřují na:

  • Určení, zda metody dodržují správné chování v případě, že ModelState je neplatný.
  • Potvrzení metody vytvoří správnou IActionResult.
  • Zkontrolujte správnost přiřazení hodnot vlastností.

Tato skupinatestůch Například GetMessagesAsync metoda je AppDbContext napodobena k vytvoření výstupu. Když metoda stránkového modelu spustí tuto metodu, vrátí napodobení výsledek. Data nepocházejí z databáze. Tím se vytvoří předvídatelné a spolehlivé testovací podmínky pro použití DAL v testech modelu stránky.

Test OnGetAsync_PopulatesThePageModel_WithAListOfMessages ukazuje, jak GetMessagesAsync je metoda napodobena pro model stránky:

var mockAppDbContext = new Mock<AppDbContext>(optionsBuilder.Options);
var expectedMessages = AppDbContext.GetSeedingMessages();
mockAppDbContext.Setup(
    db => db.GetMessagesAsync()).Returns(Task.FromResult(expectedMessages));
var pageModel = new IndexModel(mockAppDbContext.Object);

OnGetAsync Když se metoda spustí v kroku Act, volá metodu modelu GetMessagesAsync stránky.

Krok zákona o testování jednotek (tests/RazorPagesTestSample.Tests/UnitTests/IndexPageTests.cs):

// Act
await pageModel.OnGetAsync();

IndexPage metoda modelu OnGetAsync stránky (src/RazorPagesTestSample/Pages/Index.cshtml.cs):

public async Task OnGetAsync()
{
    Messages = await _db.GetMessagesAsync();
}

Metoda GetMessagesAsync v dal nevrací výsledek pro volání této metody. Napodobená verze metody vrátí výsledek.

Assert V kroku jsou skutečné zprávy (actualMessages) přiřazeny z Messages vlastnosti modelu stránky. Při přiřazení zpráv se také provede kontrola typu. Očekávané a skutečné zprávy se porovnávají podle jejich Text vlastností. Test tvrdí, že dvě List<Message> instance obsahují stejné zprávy.

// Assert
var actualMessages = Assert.IsAssignableFrom<List<Message>>(pageModel.Messages);
Assert.Equal(
    expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), 
    actualMessages.OrderBy(m => m.Id).Select(m => m.Text));

Další testy v této skupině vytvářejí objekty modelu stránky, které zahrnují DefaultHttpContextobjekty , ModelStateDictionary, ViewDataDictionaryActionContext PageContexta , a .PageContext To je užitečné při provádění testů. Aplikace zpráv například naváže ModelState chybu s AddModelError cílem zkontrolovat, jestli je při spuštění vrácena OnPostAddMessageAsync platná PageResult hodnota:

[Fact]
public async Task OnPostAddMessageAsync_ReturnsAPageResult_WhenModelStateIsInvalid()
{
    // Arrange
    var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>()
        .UseInMemoryDatabase("InMemoryDb");
    var mockAppDbContext = new Mock<AppDbContext>(optionsBuilder.Options);
    var expectedMessages = AppDbContext.GetSeedingMessages();
    mockAppDbContext.Setup(db => db.GetMessagesAsync()).Returns(Task.FromResult(expectedMessages));
    var httpContext = new DefaultHttpContext();
    var modelState = new ModelStateDictionary();
    var actionContext = new ActionContext(httpContext, new RouteData(), new PageActionDescriptor(), modelState);
    var modelMetadataProvider = new EmptyModelMetadataProvider();
    var viewData = new ViewDataDictionary(modelMetadataProvider, modelState);
    var tempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>());
    var pageContext = new PageContext(actionContext)
    {
        ViewData = viewData
    };
    var pageModel = new IndexModel(mockAppDbContext.Object)
    {
        PageContext = pageContext,
        TempData = tempData,
        Url = new UrlHelper(actionContext)
    };
    pageModel.ModelState.AddModelError("Message.Text", "The Text field is required.");

    // Act
    var result = await pageModel.OnPostAddMessageAsync();

    // Assert
    Assert.IsType<PageResult>(result);
}

Další materiály