Megosztás a következőn keresztül:


Razor Lapok egységtesztjei a ASP.NET Core-ban

ASP.NET Core támogatja a Pages-alkalmazások egységtesztjeit Razor . Az adatelérési réteg (DAL) és az oldalmodellek tesztelése a következőket biztosítja:

  • A Pages-alkalmazások részei Razor egymástól függetlenül és egységként működnek az alkalmazásépítés során.
  • Az osztályok és metódusok felelősségi köre korlátozott.
  • További dokumentáció található az alkalmazás viselkedéséről.
  • Az automatizált építés és üzembe helyezés során találhatók meg a kód frissítései által okozott regressziók.

Ez a témakör feltételezi, hogy alapszintű ismereteket szerezhet a Pages-alkalmazásokról és az Razor egységtesztekről. Ha nem ismeri a Pages-alkalmazásokat vagy a tesztelési Razor fogalmakat, tekintse meg az alábbi témaköröket:

Mintakód megtekintése vagy letöltése (hogyan töltsd le)

A mintaprojekt két alkalmazásból áll:

App Projektmappa Description
Üzenetalkalmazás src/RazorPagesTestSample Lehetővé teszi a felhasználó számára, hogy üzenetet adjon hozzá, töröljön egy üzenetet, törölje az összes üzenetet, és elemezze az üzeneteket (az üzenetenkénti szavak átlagos számának megkeresése).
Alkalmazás tesztelése tests/RazorPagesTestSample.Tests Az üzenetalkalmazás DAL és Index oldalmodelljének egységtesztelésére szolgál.

A tesztek egy IDE beépített tesztfunkcióival futtathatók, például Visual Studio. Ha Visual Studio Code-ot vagy parancssort használ, hajtsa végre a következő parancsot egy parancssorban a tests/RazorPagesTestSample.Tests mappában:

dotnet test

Üzenetalkalmazások szervezete

Az üzenetalkalmazás egy Razor Pages üzenetrendszer, amely a következő jellemzőkkel rendelkezik:

  • Az alkalmazás indexoldala (Pages/Index.cshtml és ) egy felhasználói felületet és Pages/Index.cshtml.csoldalmodell-metódust biztosít az üzenetek hozzáadásának, törlésének és elemzésének szabályozásához (az üzenetenkénti szavak átlagos számának megkereséséhez).
  • Az üzeneteket a Message osztály (Data/Message.cs) írja le két tulajdonságokkal: Id (kulcs) és Text (üzenet). A Text tulajdonság megadása kötelező, és legfeljebb 200 karakter hosszúságú lehet.
  • Az üzenetek tárolása Entity Framework memórián belüli adatbázisának† használatával történik.
  • Az alkalmazás tartalmaz egy DAL-t az adatbázis környezeti osztályában (AppDbContextData/AppDbContext.cs). A DAL metódusok megjelölve virtualvannak, ami lehetővé teszi a tesztekben való használathoz szükséges módszerek szimulálását.
  • Ha az adatbázis üres az alkalmazás indításakor, az üzenettároló három üzenettel lesz inicializálva. Ezeket a beszúrt üzeneteket a tesztekben is használják.

†Az EF-témakör, Az InMemorytesztelése című témakör bemutatja, hogyan használható memóriabeli adatbázis az MSTesttel végzett tesztekhez. Ez a témakör az xUnit tesztelési keretrendszert használja. A különböző tesztelési keretrendszerek tesztelési fogalmai és tesztelési implementációi hasonlóak, de nem azonosak.

Bár a mintaalkalmazás nem használja az adattármintát, és nem hatékony példa a Unit of Work (UoW) mintára, Razor a Pages támogatja ezeket a fejlesztési mintákat. További információ: Az infrastruktúra-adatmegőrzési réteg és atesztvezérlő logikájának tervezése a ASP.NET Core-ban (a minta implementálja az adattármintát).

Tesztalkalmazás szervezése

A tesztalkalmazás egy konzolalkalmazás a tests/RazorPagesTestSample.Tests mappában.

Alkalmazásmappa tesztelése Description
UnitTests
  • DataAccessLayerTest.cs A DAL egységtesztjeit tartalmazza.
  • IndexPageTests.cs az indexlapmodell egységtesztjeit tartalmazza.
Utilities Az adatbázis-környezet új beállításainak létrehozásához használt metódust TestDbContextOptions tartalmazza az egyes DAL-egységtesztekhez, hogy az adatbázis az egyes tesztek alapfeltételére térjen vissza.

A tesztelési keretrendszer xUnit. Az objektum-mocking keretrendszer a Moq.

Az adatelérési réteg (DAL) egységtesztjei

Az üzenetalkalmazás egy DAL-val rendelkezik, amely négy metódust tartalmaz az AppDbContext osztályban (src/RazorPagesTestSample/Data/AppDbContext.cs). Minden metódus egy vagy két egységteszttel rendelkezik a tesztalkalmazásban.

DAL metódus Function
GetMessagesAsync Adatbázisból beolvas egy List<Message>-t, amely a Text tulajdonság szerint van rendezve.
AddMessageAsync Hozzáad egy újat Message az adatbázishoz.
DeleteAllMessagesAsync Törli az adatbázis összes Message bejegyzését.
DeleteMessageAsync Egyetlen adat Message törlése az adatbázisból a következő szerint Id: .

A DAL egységtesztjeihez minden teszthez szükség van egy új DbContextOptions létrehozására AppDbContext használatával. Egy megközelítés az DbContextOptions létrehozásához az, hogy minden tesztnél egy DbContextOptionsBuilder-t használunk.

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

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

Ezzel a megközelítéssel az a probléma, hogy minden teszt az adatbázist abban az állapotban kapja meg, ahogyan az előző teszt hagyta. Ez problémás lehet, ha olyan atomi egységteszteket próbál meg írni, amelyek nem zavarják egymást. Ha azt szeretné, hogy minden AppDbContext teszt külön adatbáziskörnyezetet használjon, adjon meg egy új szolgáltatón alapuló DbContextOptions példányt. A tesztalkalmazás bemutatja, hogyan teheti ezt meg az osztálymetódusával UtilitiesTestDbContextOptions (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 A DAL egységtesztjeinek használatával minden teszt atomi módon futtatható egy friss adatbázispéldánysal:

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

Az osztály DataAccessLayerTestUnitTests/DataAccessLayerTest.cs minden tesztmódszere hasonló elrendezés-végrehajtás-igazolás mintát követ.

  1. Elrendezés: Az adatbázis a teszthez van konfigurálva, és/vagy a várt eredmény van meghatározva.
  2. Művelet: A teszt végrehajtása.
  3. Állítások: Az állításokat azért tesszük, hogy megállapítsuk, a teszt eredménye sikeres-e.

A DeleteMessageAsync metódus például felelős egyetlen, a Id által azonosított src/RazorPagesTestSample/Data/AppDbContext.cs üzenet eltávolításáért.

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

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

Ehhez a módszerhez két teszt van. Az egyik teszt ellenőrzi, hogy a metódus töröl-e egy üzenetet, ha az üzenet megtalálható az adatbázisban. A másik módszer azt ellenőrzi, hogy az adatbázis nem változik-e, ha a törlési üzenet Id nem létezik. A DeleteMessageAsync_MessageIsDeleted_WhenMessageIsFound módszer az alábbiakban látható:

[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));
    }
}

Először a metódus végrehajtja az Elrendezés lépést, ahol az Act lépés előkészítése történik. A kiinduló üzeneteket a rendszer lekérte és megtartotta seedMessages. A rendszer menti a bejuttatási üzeneteket az adatbázisba. Az üzenet Id1 törlésre van beállítva. A DeleteMessageAsync metódus végrehajtásakor a várt üzeneteknek meg kell kapniuk az összes üzenetet, kivéve azt, amelyiknek Id értéke 1. A expectedMessages változó ezt a várt eredményt jelöli.

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

A metódus működése: A DeleteMessageAsync metódust úgy hajtják végre, hogy a recId-t a 1-be adják át.

// Act
await db.DeleteMessageAsync(recId);

Végül a metódus kinyeri a Messages-t a kontextusból, és összehasonlítja a expectedMessages-el, igazolva, hogy a kettő egyenlő.

// 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));

Ahhoz, hogy összehasonlíthassuk, a kettő List<Message> megegyezik:

  • Az üzeneteket a rendszer a következő szerint rendezi Id: .
  • Az üzenetpárok a tulajdonságon Text vannak összehasonlítva.

Egy hasonló tesztmetódus ellenőrzi a DeleteMessageAsync_NoMessageIsDeleted_WhenMessageIsNotFound nem létező üzenetek törlésének eredményét. Ebben az esetben az adatbázis várt üzeneteinek meg kell egyeznie a metódus végrehajtása utáni DeleteMessageAsync tényleges üzenetel. Az adatbázis tartalmában nem lehet változás:

[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));
    }
}

Az oldalmodell módszereinek egységtesztjei

Az egységtesztek egy másik készlete felelős az oldalmodell-módszerek teszteléséért. Az üzenetalkalmazás indexlapmodelljei a IndexModel következő osztályban src/RazorPagesTestSample/Pages/Index.cshtml.cstalálhatók: .

Oldalmodell módszer Function
OnGetAsync A GetMessagesAsync metódust használva szerzi meg a felhasználói felülethez tartozó DAL-üzeneteket.
OnPostAddMessageAsync Ha a ModelState érvényes, meghívja AddMessageAsync , hogy adjon hozzá egy üzenetet az adatbázishoz.
OnPostDeleteAllMessagesAsync Az adatbázis összes üzenetének törlését kéri DeleteAllMessagesAsync .
OnPostDeleteMessageAsync Végrehajtja DeleteMessageAsync a megadott üzenet Id törlését.
OnPostAnalyzeMessagesAsync Ha egy vagy több üzenet szerepel az adatbázisban, az üzenetenkénti szavak átlagos számát számítja ki.

Az oldalmodell módszereit hét teszttel teszteljük az IndexPageTests osztályban (tests/RazorPagesTestSample.Tests/UnitTests/IndexPageTests.cs). A tesztek az ismerős Elrendezés-Cselekvés-Kijelentés mintát használják. Ezek a tesztek a következőkre összpontosítanak:

  • Annak meghatározása, hogy a metódusok a helyes viselkedést követik-e, ha a ModelState érvénytelen.
  • A metódusok megerősítése a helyes IActionResulteredményt hozza létre.
  • Annak ellenőrzése, hogy a tulajdonságérték-hozzárendelések megfelelően lettek-e elvégezve.

Ez a tesztcsoport gyakran szimulálja a DAL metódusait a várt adatok előállításához az Act lépéshez, ahol egy oldalmodell metódusát hajtják végre. A GetMessagesAsyncAppDbContext metódusa például szimulálva van a kimenet előállítására. Amikor egy oldalmodell-metódus végrehajtja ezt a metódust, a modell visszaadja az eredményt. Az adatok nem az adatbázisból származnak. Ez kiszámítható, megbízható tesztelési feltételeket hoz létre a DAL oldalmodell-tesztekben való használatához.

A OnGetAsync_PopulatesThePageModel_WithAListOfMessages teszt azt mutatja be, hogy a GetMessagesAsync függvény hogyan van mock-olva az oldalmodell számára.

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);

Amikor a OnGetAsync metódus végrehajtása az Act lépésben történik, meghívja az oldalmodell metódusát GetMessagesAsync .

Egységteszt cselekvési lépés (tests/RazorPagesTestSample.Tests/UnitTests/IndexPageTests.cs):

// Act
await pageModel.OnGetAsync();

IndexPage az oldalmodell metódusa OnGetAsync (src/RazorPagesTestSample/Pages/Index.cshtml.cs):

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

A GetMessagesAsync DAL metódusa nem adja vissza a metódushívás eredményét. A metódus szimulált verziója visszaadja az eredményt.

Ebben a Assert lépésben a tényleges üzenetek (actualMessages) az oldalmodell Messages tulajdonságából vannak hozzárendelve. Az üzenetek hozzárendelésekor a rendszer típusellenőrzést is végez. A várt és a tényleges üzenetek Text tulajdonságait hasonlítják össze. A teszt azt állítja, hogy a két List<Message> példány ugyanazokat az üzeneteket tartalmazza.

// 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));

A csoport többi tesztje olyan lapmodell-objektumokat hoz létre, amelyek tartalmazzák a DefaultHttpContext, a ModelStateDictionary, egy ActionContext a PageContext felállításához, egy ViewDataDictionary-t és egy PageContext-t. Ezek hasznosak a tesztek elvégzéséhez. Például az üzenet alkalmazás ModelState hibát állapít meg AddModelError annak ellenőrzésére, hogy PageResult végrehajtásakor érvényes OnPostAddMessageAsync kerüljön visszaadásra.

[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);
}

További erőforrások

ASP.NET Core támogatja a Pages-alkalmazások egységtesztjeit Razor . Az adatelérési réteg (DAL) és az oldalmodellek tesztelése a következőket biztosítja:

  • A Pages-alkalmazások részei Razor egymástól függetlenül és egységként működnek az alkalmazásépítés során.
  • Az osztályok és metódusok felelősségi köre korlátozott.
  • További dokumentáció található az alkalmazás viselkedéséről.
  • Az automatizált építés és üzembe helyezés során találhatók meg a kód frissítései által okozott regressziók.

Ez a témakör feltételezi, hogy alapszintű ismereteket szerezhet a Pages-alkalmazásokról és az Razor egységtesztekről. Ha nem ismeri a Pages-alkalmazásokat vagy a tesztelési Razor fogalmakat, tekintse meg az alábbi témaköröket:

Mintakód megtekintése vagy letöltése (hogyan töltsd le)

A mintaprojekt két alkalmazásból áll:

App Projektmappa Description
Üzenetalkalmazás src/RazorPagesTestSample Lehetővé teszi a felhasználó számára, hogy üzenetet adjon hozzá, töröljön egy üzenetet, törölje az összes üzenetet, és elemezze az üzeneteket (az üzenetenkénti szavak átlagos számának megkeresése).
Alkalmazás tesztelése tests/RazorPagesTestSample.Tests Az üzenetalkalmazás DAL és Index oldalmodelljének egységtesztelésére szolgál.

A tesztek egy IDE beépített tesztfunkcióival futtathatók, például Visual Studio. Ha Visual Studio Code-ot vagy parancssort használ, hajtsa végre a következő parancsot egy parancssorban a tests/RazorPagesTestSample.Tests mappában:

dotnet test

Üzenetalkalmazások szervezete

Az üzenetalkalmazás egy Razor Pages üzenetrendszer, amely a következő jellemzőkkel rendelkezik:

  • Az alkalmazás indexoldala (Pages/Index.cshtml és ) egy felhasználói felületet és Pages/Index.cshtml.csoldalmodell-metódust biztosít az üzenetek hozzáadásának, törlésének és elemzésének szabályozásához (az üzenetenkénti szavak átlagos számának megkereséséhez).
  • Az üzeneteket a Message osztály (Data/Message.cs) írja le két tulajdonságokkal: Id (kulcs) és Text (üzenet). A Text tulajdonság megadása kötelező, és legfeljebb 200 karakter hosszúságú lehet.
  • Az üzenetek tárolása Entity Framework memórián belüli adatbázisának† használatával történik.
  • Az alkalmazás tartalmaz egy DAL-t az adatbázis környezeti osztályában (AppDbContextData/AppDbContext.cs). A DAL metódusok megjelölve virtualvannak, ami lehetővé teszi a tesztekben való használathoz szükséges módszerek szimulálását.
  • Ha az adatbázis üres az alkalmazás indításakor, az üzenettároló három üzenettel lesz inicializálva. Ezeket a beszúrt üzeneteket a tesztekben is használják.

†Az EF-témakör, Az InMemorytesztelése című témakör bemutatja, hogyan használható memóriabeli adatbázis az MSTesttel végzett tesztekhez. Ez a témakör az xUnit tesztelési keretrendszert használja. A különböző tesztelési keretrendszerek tesztelési fogalmai és tesztelési implementációi hasonlóak, de nem azonosak.

Bár a mintaalkalmazás nem használja az adattármintát, és nem hatékony példa a Unit of Work (UoW) mintára, Razor a Pages támogatja ezeket a fejlesztési mintákat. További információ: Az infrastruktúra-adatmegőrzési réteg és atesztvezérlő logikájának tervezése a ASP.NET Core-ban (a minta implementálja az adattármintát).

Tesztalkalmazás szervezése

A tesztalkalmazás egy konzolalkalmazás a tests/RazorPagesTestSample.Tests mappában.

Alkalmazásmappa tesztelése Description
UnitTests
  • DataAccessLayerTest.cs A DAL egységtesztjeit tartalmazza.
  • IndexPageTests.cs az indexlapmodell egységtesztjeit tartalmazza.
Utilities Az adatbázis-környezet új beállításainak létrehozásához használt metódust TestDbContextOptions tartalmazza az egyes DAL-egységtesztekhez, hogy az adatbázis az egyes tesztek alapfeltételére térjen vissza.

A tesztelési keretrendszer xUnit. Az objektum-mocking keretrendszer a Moq.

Az adatelérési réteg (DAL) egységtesztjei

Az üzenetalkalmazás egy DAL-val rendelkezik, amely négy metódust tartalmaz az AppDbContext osztályban (src/RazorPagesTestSample/Data/AppDbContext.cs). Minden metódus egy vagy két egységteszttel rendelkezik a tesztalkalmazásban.

DAL metódus Function
GetMessagesAsync Adatbázisból beolvas egy List<Message>-t, amely a Text tulajdonság szerint van rendezve.
AddMessageAsync Hozzáad egy újat Message az adatbázishoz.
DeleteAllMessagesAsync Törli az adatbázis összes Message bejegyzését.
DeleteMessageAsync Egyetlen adat Message törlése az adatbázisból a következő szerint Id: .

A DAL egységtesztjeihez minden teszthez szükség van egy új DbContextOptions létrehozására AppDbContext használatával. Egy megközelítés az DbContextOptions létrehozásához az, hogy minden tesztnél egy DbContextOptionsBuilder-t használunk.

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

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

Ezzel a megközelítéssel az a probléma, hogy minden teszt az adatbázist abban az állapotban kapja meg, ahogyan az előző teszt hagyta. Ez problémás lehet, ha olyan atomi egységteszteket próbál meg írni, amelyek nem zavarják egymást. Ha azt szeretné, hogy minden AppDbContext teszt külön adatbáziskörnyezetet használjon, adjon meg egy új szolgáltatón alapuló DbContextOptions példányt. A tesztalkalmazás bemutatja, hogyan teheti ezt meg az osztálymetódusával UtilitiesTestDbContextOptions (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 A DAL egységtesztjeinek használatával minden teszt atomi módon futtatható egy friss adatbázispéldánysal:

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

Az osztály DataAccessLayerTestUnitTests/DataAccessLayerTest.cs minden tesztmódszere hasonló elrendezés-végrehajtás-igazolás mintát követ.

  1. Elrendezés: Az adatbázis a teszthez van konfigurálva, és/vagy a várt eredmény van meghatározva.
  2. Művelet: A teszt végrehajtása.
  3. Állítások: Az állításokat azért tesszük, hogy megállapítsuk, a teszt eredménye sikeres-e.

A DeleteMessageAsync metódus például felelős egyetlen, a Id által azonosított src/RazorPagesTestSample/Data/AppDbContext.cs üzenet eltávolításáért.

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

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

Ehhez a módszerhez két teszt van. Az egyik teszt ellenőrzi, hogy a metódus töröl-e egy üzenetet, ha az üzenet megtalálható az adatbázisban. A másik módszer azt ellenőrzi, hogy az adatbázis nem változik-e, ha a törlési üzenet Id nem létezik. A DeleteMessageAsync_MessageIsDeleted_WhenMessageIsFound módszer az alábbiakban látható:

[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));
    }
}

Először a metódus végrehajtja az Elrendezés lépést, ahol az Act lépés előkészítése történik. A kiinduló üzeneteket a rendszer lekérte és megtartotta seedMessages. A rendszer menti a bejuttatási üzeneteket az adatbázisba. Az üzenet Id1 törlésre van beállítva. A DeleteMessageAsync metódus végrehajtásakor a várt üzeneteknek meg kell kapniuk az összes üzenetet, kivéve azt, amelyiknek Id értéke 1. A expectedMessages változó ezt a várt eredményt jelöli.

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

A metódus működése: A DeleteMessageAsync metódust úgy hajtják végre, hogy a recId-t a 1-be adják át.

// Act
await db.DeleteMessageAsync(recId);

Végül a metódus kinyeri a Messages-t a kontextusból, és összehasonlítja a expectedMessages-el, igazolva, hogy a kettő egyenlő.

// 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));

Ahhoz, hogy összehasonlíthassuk, a kettő List<Message> megegyezik:

  • Az üzeneteket a rendszer a következő szerint rendezi Id: .
  • Az üzenetpárok a tulajdonságon Text vannak összehasonlítva.

Egy hasonló tesztmetódus ellenőrzi a DeleteMessageAsync_NoMessageIsDeleted_WhenMessageIsNotFound nem létező üzenetek törlésének eredményét. Ebben az esetben az adatbázis várt üzeneteinek meg kell egyeznie a metódus végrehajtása utáni DeleteMessageAsync tényleges üzenetel. Az adatbázis tartalmában nem lehet változás:

[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));
    }
}

Az oldalmodell módszereinek egységtesztjei

Az egységtesztek egy másik készlete felelős az oldalmodell-módszerek teszteléséért. Az üzenetalkalmazás indexlapmodelljei a IndexModel következő osztályban src/RazorPagesTestSample/Pages/Index.cshtml.cstalálhatók: .

Oldalmodell módszer Function
OnGetAsync A GetMessagesAsync metódust használva szerzi meg a felhasználói felülethez tartozó DAL-üzeneteket.
OnPostAddMessageAsync Ha a ModelState érvényes, meghívja AddMessageAsync , hogy adjon hozzá egy üzenetet az adatbázishoz.
OnPostDeleteAllMessagesAsync Az adatbázis összes üzenetének törlését kéri DeleteAllMessagesAsync .
OnPostDeleteMessageAsync Végrehajtja DeleteMessageAsync a megadott üzenet Id törlését.
OnPostAnalyzeMessagesAsync Ha egy vagy több üzenet szerepel az adatbázisban, az üzenetenkénti szavak átlagos számát számítja ki.

Az oldalmodell módszereit hét teszttel teszteljük az IndexPageTests osztályban (tests/RazorPagesTestSample.Tests/UnitTests/IndexPageTests.cs). A tesztek az ismerős Elrendezés-Cselekvés-Kijelentés mintát használják. Ezek a tesztek a következőkre összpontosítanak:

  • Annak meghatározása, hogy a metódusok a helyes viselkedést követik-e, ha a ModelState érvénytelen.
  • A metódusok megerősítése a helyes IActionResulteredményt hozza létre.
  • Annak ellenőrzése, hogy a tulajdonságérték-hozzárendelések megfelelően lettek-e elvégezve.

Ez a tesztcsoport gyakran szimulálja a DAL metódusait a várt adatok előállításához az Act lépéshez, ahol egy oldalmodell metódusát hajtják végre. A GetMessagesAsyncAppDbContext metódusa például szimulálva van a kimenet előállítására. Amikor egy oldalmodell-metódus végrehajtja ezt a metódust, a modell visszaadja az eredményt. Az adatok nem az adatbázisból származnak. Ez kiszámítható, megbízható tesztelési feltételeket hoz létre a DAL oldalmodell-tesztekben való használatához.

A OnGetAsync_PopulatesThePageModel_WithAListOfMessages teszt azt mutatja be, hogy a GetMessagesAsync függvény hogyan van mock-olva az oldalmodell számára.

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);

Amikor a OnGetAsync metódus végrehajtása az Act lépésben történik, meghívja az oldalmodell metódusát GetMessagesAsync .

Egységteszt cselekvési lépés (tests/RazorPagesTestSample.Tests/UnitTests/IndexPageTests.cs):

// Act
await pageModel.OnGetAsync();

IndexPage az oldalmodell metódusa OnGetAsync (src/RazorPagesTestSample/Pages/Index.cshtml.cs):

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

A GetMessagesAsync DAL metódusa nem adja vissza a metódushívás eredményét. A metódus szimulált verziója visszaadja az eredményt.

Ebben a Assert lépésben a tényleges üzenetek (actualMessages) az oldalmodell Messages tulajdonságából vannak hozzárendelve. Az üzenetek hozzárendelésekor a rendszer típusellenőrzést is végez. A várt és a tényleges üzenetek Text tulajdonságait hasonlítják össze. A teszt azt állítja, hogy a két List<Message> példány ugyanazokat az üzeneteket tartalmazza.

// 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));

A csoport többi tesztje olyan lapmodell-objektumokat hoz létre, amelyek tartalmazzák a DefaultHttpContext, a ModelStateDictionary, egy ActionContext a PageContext felállításához, egy ViewDataDictionary-t és egy PageContext-t. Ezek hasznosak a tesztek elvégzéséhez. Például az üzenet alkalmazás ModelState hibát állapít meg AddModelError annak ellenőrzésére, hogy PageResult végrehajtásakor érvényes OnPostAddMessageAsync kerüljön visszaadásra.

[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);
}

További erőforrások