Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Készítette: Jos van der Til, Martin Costello, és Javier Calvarro Nelson.
Az integrációs tesztek biztosítják, hogy az alkalmazás összetevői megfelelően működjenek olyan szinten, amely magában foglalja az alkalmazás támogató infrastruktúráját, például az adatbázist, a fájlrendszert és a hálózatot. ASP.NET Core támogatja az integrációs teszteket egy tesztwebhelyen és egy memóriabeli tesztkiszolgálón futó egységteszt-keretrendszer használatával.
Ez a cikk feltételezi az egységtesztek alapszintű megértését. Ha nem ismeri a tesztelési fogalmakat, tekintse meg a tesztelés a .NET-ben című cikket és annak csatolt tartalmát.
Mintakód megtekintése vagy letöltése (hogyan kell letölteni)
A mintaalkalmazás egy Razor Pages-alkalmazás, és feltételezi, hogy alapszintű ismeretekkel rendelkezik a Razor Pages-ről. Ha nem ismeri a Razor oldalak tartalmát, olvassa el a következő cikkeket.
Az SLA-kteszteléséhez olyan eszközt ajánlunk, mint például a .NET-Playwright, amely automatizálhatja a böngészőt.
Bevezetés az integrációs tesztekbe
Az integrációs tesztek az alkalmazás összetevőit szélesebb szinten értékelik ki, mint egységtesztek. Az egységtesztek izolált szoftverösszetevők, például az egyes osztálymódszerek tesztelésére szolgálnak. Az integrációs tesztek megerősítik, hogy két vagy több alkalmazásösszetevő együttműködik a várt eredmény érdekében, beleértve a kérések teljes feldolgozásához szükséges összes összetevőt is.
Ezek a szélesebb körű tesztek az alkalmazás infrastruktúrájának és teljes keretrendszerének tesztelésére szolgálnak, gyakran a következő összetevőket is beleértve:
- Adatbázis
- Fájlrendszer
- Hálózati berendezések
- Kérelem-válasz folyamat
Az egységtesztek az infrastruktúra-összetevők helyett létrehozott összetevőket használnak, más néven hamis vagy szimulált objektumokat.
Az egységtesztekkel ellentétben az integrációs tesztek:
- Használja azokat a tényleges összetevőket, amelyeket az alkalmazás éles környezetben használ.
- További kód- és adatfeldolgozást igényel.
- A futtatás hosszabb időt vesz igénybe.
Ezért az integrációs tesztek használatának korlátozása a legfontosabb infrastruktúra-forgatókönyvekre. Ha egy viselkedést egységteszt vagy integrációs teszt használatával lehet tesztelni, válassza ki az egységtesztet.
Az integrációs tesztek megvitatása során a tesztelt projektet gyakran nevezik System Under Test, vagy röviden "SUT" néven. A cikk során a "SUT" a tesztelt ASP.NET Core-alkalmazásra hivatkozik.
Ne írjon integrációs teszteket az adatbázisok és fájlrendszerek minden permutációs adat- és fájlhozzáféréshez. Függetlenül attól, hogy az alkalmazások hány helyen használják az adatbázisokat és a fájlrendszereket, az olvasási, írási, frissítési és törlési integrációs tesztek koncentrált készlete általában képes megfelelően tesztelni az adatbázis- és fájlrendszer-összetevőket. Használjon egységteszteket az ezen összetevőkkel interakcióba lépő metóduslogika rutintesztjeihez. Az egységtesztekben az infrastruktúra hamis vagy hamisított használata gyorsabb tesztvégrehajtást eredményez.
ASP.NET Core integrációs tesztek
A ASP.NET Core integrációs tesztjeihez a következők szükségesek:
- A tesztprojektek a teszteket tartalmazzák és hajtják végre. A tesztprojekt hivatkozással rendelkezik a SUT-ra.
- A tesztprojekt létrehoz egy tesztwebhely-gazdagépet a SUT számára, és egy tesztkiszolgáló-ügyféllel kezeli a kérelmeket és a válaszokat a SUT-val.
- A tesztfuttató a tesztek végrehajtására és a teszteredmények jelentésére szolgál.
Az integrációs tesztek olyan események sorozatát követik, amelyek magukban foglalják a szokásos Előkészület, Végrehajtásés Ellenőrzés tesztelési lépéseket.
- Az SUT webhoszt konfigurálva van.
- Létrejön egy tesztkiszolgáló-ügyfél, amely kéréseket küld az alkalmazásnak.
- A Arrange tesztelési lépés végrehajtása: A tesztalkalmazás előkészít egy kérést.
- A törvény tesztlépés végrehajtása: Az ügyfél elküldi a kérést, és megkapja a választ.
- A ellenőrzési tesztlépést végrehajtják: A tényleges választ a rendszer a várt válasz alapján érvényesíti, és érvényesnek nyilvánítja (), vagy sikertelennek ().
- A folyamat az összes teszt végrehajtásáig folytatódik.
- A teszteredmények jelentésre kerültek.
A teszt webgazda általában eltérő konfigurációval van beállítva, mint az alkalmazás normál webgazdája a tesztek futtatásához. Előfordulhat például, hogy a tesztekhez egy másik adatbázist vagy különböző alkalmazásbeállításokat használnak.
Az infrastruktúra-összetevőket, például a tesztweb-gazdagépet és a memóriabeli tesztkiszolgálót (TestServer) a Microsoft.AspNetCore.Mvc.Testing csomag biztosítja vagy felügyeli. A csomag használata leegyszerűsíti a tesztek létrehozását és végrehajtását.
A Microsoft.AspNetCore.Mvc.Testing
csomag a következő feladatokat kezeli:
- Másolja a függőségi fájlt (
.deps
) a SUT-ból a tesztprojektbin
könyvtárába. - A tartalomgyökerét-re a SUT projektgyökeréhez állítja, hogy a statikus fájlok és a lapok/nézetek megtalálhatók legyenek a tesztek végrehajtásakor.
- A WebApplicationFactory osztály gördülékenyebbé teszi a SUT
TestServer
használatával történő rendszerindítását.
Az egységtesztek dokumentációja ismerteti a tesztprojektek és tesztfuttatók beállításának módját, valamint a tesztek futtatásának részletes utasításait, valamint a tesztek és tesztosztályok elnevezésére vonatkozó javaslatokat.
Egységtesztek elkülönítése az integrációs tesztektől különböző projektekre. A tesztek elkülönítése:
- Segít biztosítani, hogy az infrastruktúra tesztelési összetevői véletlenül ne legyenek belefoglalva az egységtesztekbe.
- Lehetővé teszi a tesztkészlet futtatásának szabályozását.
Gyakorlatilag nincs különbség a Razor Pages-alkalmazások és az MVC-alkalmazások tesztjeinek konfigurációja között. Az egyetlen különbség a tesztek elnevezésében van. Egy Razor Pages-alkalmazásban az oldalvégpontok tesztjeit általában az oldalmodell-osztályról nevezik el (például IndexPageTests
az indexlap összetevőintegrációjának teszteléséhez). Az MVC-alkalmazásokban a tesztek általában vezérlőosztályok szerint vannak rendszerezve, és az általuk tesztelt vezérlőkről nevezik el őket (például HomeControllerTests
a Home vezérlő összetevőintegrációjának teszteléséhez).
Alkalmazás előfeltételeinek tesztelése
A tesztprojektnek a következőnek kell lennie:
- Hivatkozzon a
Microsoft.AspNetCore.Mvc.Testing
csomagra. - Adja meg a webes SDK-t a projektfájlban (
<Project Sdk="Microsoft.NET.Sdk.Web">
).
Ezek az előfeltételek láthatók a mintaalkalmazás-ban. Vizsgálja meg a tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj
fájlt. A mintaalkalmazás az xUnit tesztelési keretrendszert és a AngleSharp elemzőtárat használja, így a mintaalkalmazás a következőre is hivatkozik:
A xunit.runner.visualstudio
2.4.2-es vagy újabb verzióját használó alkalmazásokban a tesztprojektnek hivatkoznia kell a Microsoft.NET.Test.Sdk
csomagra.
Az Entity Framework Core a tesztekben is használatos. Tekintse meg a projektfájlt a GitHub.
SUT-környezet
Ha a SUT környezet nincs beállítva, a környezet alapértelmezés szerint a Fejlesztési környezet.
Alapszintű tesztek az alapértelmezett WebApplicationFactory használatával
A WebApplicationFactory<TEntryPoint>-t használjuk egy TestServer létrehozásához az integrációs tesztekhez.
TEntryPoint
a SUT belépési pontosztálya, általában Program.cs
.
A tesztosztályok implementálnak egy osztály-fix technikai felületet (IClassFixture
), amely jelzi, hogy az osztály teszteket tartalmaz, és megosztott objektumpéldányokat biztosít az osztály tesztjei során.
Az alábbi tesztosztály BasicTests
a WebApplicationFactory
használatával végzi a SUT inicializálását, és biztosít egy HttpClient-t a Get_EndpointsReturnSuccessAndCorrectContentType
tesztmetódus számára. A metódus ellenőrzi, hogy a válasz állapotkódja sikeres-e (200-299), és a Content-Type
fejléc több alkalmazásoldalon text/html; charset=utf-8
.
CreateClient() létrehoz egy HttpClient
-példányt, amely automatikusan követi az átirányításokat és kezeli a cookie-kat.
public class BasicTests
: IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
public BasicTests(WebApplicationFactory<Program> factory)
{
_factory = factory;
}
[Theory]
[InlineData("/")]
[InlineData("/Index")]
[InlineData("/About")]
[InlineData("/Privacy")]
[InlineData("/Contact")]
public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.Equal("text/html; charset=utf-8",
response.Content.Headers.ContentType.ToString());
}
}
[TestClass]
public class BasicTests
{
private static CustomWebApplicationFactory<Program> _factory;
[ClassInitialize]
public static void AssemblyInitialize(TestContext _)
{
_factory = new CustomWebApplicationFactory<Program>();
}
[ClassCleanup(ClassCleanupBehavior.EndOfClass)]
public static void AssemblyCleanup(TestContext _)
{
_factory.Dispose();
}
[TestMethod]
[DataRow("/")]
[DataRow("/Index")]
[DataRow("/About")]
[DataRow("/Privacy")]
[DataRow("/Contact")]
public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.AreEqual("text/html; charset=utf-8",
response.Content.Headers.ContentType.ToString());
}
}
public class BasicTests
{
private CustomWebApplicationFactory<Program>
_factory;
[SetUp]
public void SetUp()
{
_factory = new CustomWebApplicationFactory<Program>();
}
[TearDown]
public void TearDown()
{
_factory.Dispose();
}
[DatapointSource]
public string[] values = ["/", "/Index", "/About", "/Privacy", "/Contact"];
[Theory]
public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.That(response.Content.Headers.ContentType.ToString(), Is.EqualTo("text/html; charset=utf-8"));
}
}
Alapértelmezés szerint a nem alapvető cookie-k nem maradnak meg a kérések között, ha engedélyezve van az általános adatvédelmi rendelet hozzájárulási szabályzata. A nem alapvető cookie-k( például a TempData-szolgáltató által használt) megőrzése érdekében jelölje meg őket alapvetőként a tesztek során. A cookie alapvetőként való megjelölésére vonatkozó utasításokért lásd: Alapvető cookie-k.
AngleSharp és Application Parts
az antiforgery-ellenőrzésekhez
Ez a cikk a AngleSharp elemzőt használja az antiforgery-ellenőrzések kezelésére oldalak betöltése és a HTML elemzése révén. A vezérlő végpontjainak és a Razor oldalak nézeteinek részletesebb teszteléséhez, anélkül, hogy törődne azzal, hogyan jelennek meg a böngészőben, fontolja meg a Application Parts
használatát. Az Alkalmazásrészek megközelítés egy vezérlőt vagy Razor lapot injektál az alkalmazásba, amellyel JSON-kéréseket lehet küldeni a szükséges értékek lekéréséhez. További információért lásd a blogot: Integrációs tesztelés ASP.NET Core erőforrások Antiforgery-vel történő védelme az Alkalmazás Részei segítségével és a kapcsolódó GitHub-adattár szerzője: Martin Costello.
A WebApplicationFactory testreszabása
A webtárhely konfigurációja a tesztosztályoktól függetlenül hozható létre, úgy, hogy a WebApplicationFactory<TEntryPoint>-ból örökölt egy vagy több egyéni gyárat készítünk.
Örököljön
WebApplicationFactory
és bírálja felül ConfigureWebHost. A IWebHostBuilder lehetővé teszi a szolgáltatásgyűjtemény konfigurálásátIWebHostBuilder.ConfigureServices
public class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<TProgram> where TProgram : class { protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureServices(services => { var dbContextDescriptor = services.SingleOrDefault( d => d.ServiceType == typeof(IDbContextOptionsConfiguration<ApplicationDbContext>)); services.Remove(dbContextDescriptor); var dbConnectionDescriptor = services.SingleOrDefault( d => d.ServiceType == typeof(DbConnection)); services.Remove(dbConnectionDescriptor); // Create open SqliteConnection so EF won't automatically close it. services.AddSingleton<DbConnection>(container => { var connection = new SqliteConnection("DataSource=:memory:"); connection.Open(); return connection; }); services.AddDbContext<ApplicationDbContext>((container, options) => { var connection = container.GetRequiredService<DbConnection>(); options.UseSqlite(connection); }); }); builder.UseEnvironment("Development"); } }
public class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<TProgram> where TProgram : class { protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureServices(services => { var dbContextDescriptor = services.SingleOrDefault( d => d.ServiceType == typeof(IDbContextOptionsConfiguration<ApplicationDbContext>)); services.Remove(dbContextDescriptor); var dbConnectionDescriptor = services.SingleOrDefault( d => d.ServiceType == typeof(DbConnection)); services.Remove(dbConnectionDescriptor); // Create open SqliteConnection so EF won't automatically close it. services.AddSingleton<DbConnection>(container => { var connection = new SqliteConnection("DataSource=:memory:"); connection.Open(); return connection; }); services.AddDbContext<ApplicationDbContext>((container, options) => { var connection = container.GetRequiredService<DbConnection>(); options.UseSqlite(connection); }); }); builder.UseEnvironment("Development"); } }
public class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<TProgram> where TProgram : class { protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureServices(services => { var dbContextDescriptor = services.SingleOrDefault( d => d.ServiceType == typeof(IDbContextOptionsConfiguration<ApplicationDbContext>)); services.Remove(dbContextDescriptor); var dbConnectionDescriptor = services.SingleOrDefault( d => d.ServiceType == typeof(DbConnection)); services.Remove(dbConnectionDescriptor); // Create open SqliteConnection so EF won't automatically close it. services.AddSingleton<DbConnection>(container => { var connection = new SqliteConnection("DataSource=:memory:"); connection.Open(); return connection; }); services.AddDbContext<ApplicationDbContext>((container, options) => { var connection = container.GetRequiredService<DbConnection>(); options.UseSqlite(connection); }); }); builder.UseEnvironment("Development"); } }
Az adatbázis-vetést a mintaalkalmazásban a
InitializeDbForTests
metódus végzi. A módszert az integrációs tesztek mintájában ismertetjük: Az alkalmazás szervezetének tesztelése szakasz.A SUT adatbázis-környezetét regisztrálták
Program.cs
-ben. A tesztalkalmazásbuilder.ConfigureServices
visszahívása az alkalmazás lesz végrehajtva. Ha más adatbázist szeretne használni a tesztekhez, mint az alkalmazás adatbázisa, az alkalmazás adatbázis-környezetét abuilder.ConfigureServices
adatbázis-környezetre kell cserélni.A mintaalkalmazás megkeresi az adatbázis-környezet szolgáltatásleíróját, és a leíró használatával eltávolítja a szolgáltatásregisztrációt. A gyár ezután hozzáad egy új
ApplicationDbContext
, amely memórián belüli adatbázist használ a tesztekhez.Ha másik adatbázishoz szeretne csatlakozni, módosítsa a
DbConnection
. SQL Server tesztadatbázis használata:- A projektfájlban hivatkozzon a
Microsoft.EntityFrameworkCore.SqlServer
NuGet-csomagra. - Hívja
UseInMemoryDatabase
.
- A projektfájlban hivatkozzon a
Az egyéni
CustomWebApplicationFactory
-t használja a tesztosztályokban. Az alábbi példa a gyárat használja aIndexPageTests
osztályban:public class IndexPageTests : IClassFixture<CustomWebApplicationFactory<Program>> { private readonly HttpClient _client; private readonly CustomWebApplicationFactory<Program> _factory; public IndexPageTests( CustomWebApplicationFactory<Program> factory) { _factory = factory; _client = factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false }); }
[TestClass] public class IndexPageTests { private static HttpClient _client; private static CustomWebApplicationFactory<Program> _factory; [ClassInitialize] public static void AssemblyInitialize(TestContext _) { _factory = new CustomWebApplicationFactory<Program>(); _client = _factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false }); } [ClassCleanup(ClassCleanupBehavior.EndOfClass)] public static void AssemblyCleanup(TestContext _) { _factory.Dispose(); }
public class IndexPageTests { private HttpClient _client; private CustomWebApplicationFactory<Program> _factory; [SetUp] public void SetUp() { _factory = new CustomWebApplicationFactory<Program>(); _client = _factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false }); } [TearDown] public void TearDown() { _factory.Dispose(); _client.Dispose(); }
A mintaalkalmazás ügyfele úgy van konfigurálva, hogy megakadályozza a
HttpClient
átirányítások követését. Amint azt a Mock authentication szakasz későbbi szakasza ismerteti, ez lehetővé teszi a tesztek számára az alkalmazás első válaszának eredményének ellenőrzését. Az első válasz ezek közül sok tesztben egy átirányítás egy "Location
" fejléccel.Egy tipikus teszt a
HttpClient
és segédmetenek használatával dolgozza fel a kérést és a választ:[Fact] public async Task Post_DeleteAllMessagesHandler_ReturnsRedirectToRoot() { // Arrange var defaultPage = await _client.GetAsync("/"); var content = await HtmlHelpers.GetDocumentAsync(defaultPage); //Act var response = await _client.SendAsync( (IHtmlFormElement)content.QuerySelector("form[id='messages']"), (IHtmlButtonElement)content.QuerySelector("button[id='deleteAllBtn']")); // Assert Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode); Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); Assert.Equal("/", response.Headers.Location.OriginalString); }
[TestMethod] public async Task Post_DeleteAllMessagesHandler_ReturnsRedirectToRoot() { // Arrange var defaultPage = await _client.GetAsync("/"); var content = await HtmlHelpers.GetDocumentAsync(defaultPage); //Act var response = await _client.SendAsync( (IHtmlFormElement)content.QuerySelector("form[id='messages']"), (IHtmlButtonElement)content.QuerySelector("button[id='deleteAllBtn']")); // Assert Assert.AreEqual(HttpStatusCode.OK, defaultPage.StatusCode); Assert.AreEqual(HttpStatusCode.Redirect, response.StatusCode); Assert.AreEqual("/", response.Headers.Location.OriginalString); }
[Test] public async Task Post_DeleteAllMessagesHandler_ReturnsRedirectToRoot() { // Arrange var defaultPage = await _client.GetAsync("/"); var content = await HtmlHelpers.GetDocumentAsync(defaultPage); //Act var response = await _client.SendAsync( (IHtmlFormElement)content.QuerySelector("form[id='messages']"), (IHtmlButtonElement)content.QuerySelector("button[id='deleteAllBtn']")); // Assert Assert.That(defaultPage.StatusCode, Is.EqualTo(HttpStatusCode.OK)); Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Redirect)); Assert.That(response.Headers.Location.OriginalString, Is.EqualTo("/")); }
A SUT-nak küldött POST-kéréseknek meg kell felelniük az alkalmazás adatvédelmi rendszerének automatikusan végrehajtott hamisítás elleni ellenőrzési folyamatának. A teszt POST-kérésének rendezéséhez a tesztalkalmazásnak a következőnek kell lennie:
- Kérjen egy kérelmet az oldalhoz.
- Elemezze az azonosítóhamisítás elleni cookie-t, és kérje le az érvényesítési jogkivonatot a választól.
- Végezze el a POST kérést az antiforgery cookie és az érvényesítési jogkivonattal.
A mintaalkalmazás SendAsync
segédmetódusai (Helpers/HttpClientExtensions.cs
) és a GetDocumentAsync
segédmetódus (Helpers/HtmlHelpers.cs
) az AngleSharp elemzővel kezelik az antiforgery-ellenőrzést az alábbi módszerekkel:
-
GetDocumentAsync
: Megkapja a HttpResponseMessage, és visszaad egyIHtmlDocument
.GetDocumentAsync
egy olyan gyárat használ, amely az eredeti alapján készít elő egyHttpResponseMessage
-t. További információért tekintse meg az AngleSharp dokumentációt. -
SendAsync
kiterjesztési metódusai aHttpClient
összállítanak egy HttpRequestMessage-t, és meghívják a SendAsync(HttpRequestMessage)-t, hogy kérelmeket küldjenek a SUT-nak. ASendAsync
túlterhelései elfogadják a HTML-űrlapot (IHtmlFormElement
) és a következőket:- Az űrlap Küldés gombja (
IHtmlElement
) - Űrlapértékek gyűjtése (
IEnumerable<KeyValuePair<string, string>>
) - Küldés gomb (
IHtmlElement
) és űrlapértékek (IEnumerable<KeyValuePair<string, string>>
)
- Az űrlap Küldés gombja (
AngleSharp egy harmadik féltől származó elemzési kódtár, amelyet bemutató célokra használnak, ebben a cikkben és a mintaalkalmazásban. Az AngleSharp nem támogatott vagy szükséges ASP.NET Core-alkalmazások integrációs teszteléséhez. Más elemzők is használhatók, például a Html Agility Pack (HAP). Egy másik módszer, ha olyan kódot írunk, amely közvetlenül kezeli az antiforgery rendszer kérés-ellenőrzési tokenjét és az antiforgery cookie-t. További információkért tekintse meg az antiforgery-ellenőrzésekről szóló cikket itt: AngleSharp vs Application Parts
,.
A EF-Core memóriabeli adatbázis-szolgáltató korlátozott és alapszintű teszteléshez használható, de a SQLite-szolgáltató a memóriabeli teszteléshez ajánlott.
Lásd: Indítás kiterjesztése indítási szűrőkkel, amely bemutatja, hogyan konfigurálhat köztes szoftvereket IStartupFilterhasználatával, ami akkor hasznos, ha egy teszthez egyéni szolgáltatásra vagy köztes szoftverre van szükség.
Az ügyfél testreszabása a WithWebHostBuilder használatával
Ha egy tesztmetóduson belül további konfigurációra van szükség, WithWebHostBuilder létrehoz egy új WebApplicationFactory
egy olyan IWebHostBuilder-vel, amelyet a konfiguráció további testreszab.
A mintakód meghívja WithWebHostBuilder
, hogy a konfigurált szolgáltatásokat tesztcsomókkal cserélje le. További információért és a példák használatáért lásd a Modellszolgáltatások injektálása rész ebben a cikkben.
A mintaalkalmazás Post_DeleteMessageHandler_ReturnsRedirectToRoot
tesztmetódusa a WithWebHostBuilder
használatát mutatja be. Ez a teszt egy rekord törlését hajtja végre az adatbázisban egy űrlapbeküldés SUT-ban való aktiválásával.
Mivel a IndexPageTests
osztály egy másik tesztje olyan műveletet hajt végre, amely törli az adatbázis összes rekordját, és a Post_DeleteMessageHandler_ReturnsRedirectToRoot
metódus előtt futhat, az adatbázist ebben a tesztmetódusban újból el kell végezni, hogy a rendszer egy rekordot adjon meg a SUT számára a törléshez. A SUT-ban a messages
űrlap első törlési gombjának kiválasztását a rendszer szimulálja a SUT-nak küldött kérésben:
[Fact]
public async Task Post_DeleteMessageHandler_ReturnsRedirectToRoot()
{
// Arrange
using (var scope = _factory.Services.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<ApplicationDbContext>();
Utilities.ReinitializeDbForTests(db);
}
var defaultPage = await _client.GetAsync("/");
var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
//Act
var response = await _client.SendAsync(
(IHtmlFormElement)content.QuerySelector("form[id='messages']"),
(IHtmlButtonElement)content.QuerySelector("form[id='messages']")
.QuerySelector("div[class='panel-body']")
.QuerySelector("button"));
// Assert
Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode);
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.Equal("/", response.Headers.Location.OriginalString);
}
[TestMethod]
public async Task Post_DeleteMessageHandler_ReturnsRedirectToRoot()
{
// Arrange
using (var scope = _factory.Services.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<ApplicationDbContext>();
Utilities.ReinitializeDbForTests(db);
}
var defaultPage = await _client.GetAsync("/");
var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
//Act
var response = await _client.SendAsync(
(IHtmlFormElement)content.QuerySelector("form[id='messages']"),
(IHtmlButtonElement)content.QuerySelector("form[id='messages']")
.QuerySelector("div[class='panel-body']")
.QuerySelector("button"));
// Assert
Assert.AreEqual(HttpStatusCode.OK, defaultPage.StatusCode);
Assert.AreEqual(HttpStatusCode.Redirect, response.StatusCode);
Assert.AreEqual("/", response.Headers.Location.OriginalString);
}
[Test]
public async Task Post_DeleteMessageHandler_ReturnsRedirectToRoot()
{
// Arrange
using (var scope = _factory.Services.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<ApplicationDbContext>();
Utilities.ReinitializeDbForTests(db);
}
var defaultPage = await _client.GetAsync("/");
var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
//Act
var response = await _client.SendAsync(
(IHtmlFormElement)content.QuerySelector("form[id='messages']"),
(IHtmlButtonElement)content.QuerySelector("form[id='messages']")
.QuerySelector("div[class='panel-body']")
.QuerySelector("button"));
// Assert
Assert.That(defaultPage.StatusCode, Is.EqualTo(HttpStatusCode.OK));
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Redirect));
Assert.That(response.Headers.Location.OriginalString, Is.EqualTo("/"));
}
Ügyfélbeállítások
A WebApplicationFactoryClientOptions példányok létrehozásakor az alapértelmezett beállításokat és az elérhető beállításokat a HttpClient
lapon tekinti meg.
Hozza létre a WebApplicationFactoryClientOptions
osztályt, és adja át a CreateClient() metódusnak:
public class IndexPageTests :
IClassFixture<CustomWebApplicationFactory<Program>>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory<Program>
_factory;
public IndexPageTests(
CustomWebApplicationFactory<Program> factory)
{
_factory = factory;
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
}
[TestClass]
public class IndexPageTests
{
private static HttpClient _client;
private static CustomWebApplicationFactory<Program>
_factory;
[ClassInitialize]
public static void AssemblyInitialize(TestContext _)
{
_factory = new CustomWebApplicationFactory<Program>();
_client = _factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
}
[ClassCleanup(ClassCleanupBehavior.EndOfClass)]
public static void AssemblyCleanup(TestContext _)
{
_factory.Dispose();
}
public class IndexPageTests
{
private HttpClient _client;
private CustomWebApplicationFactory<Program>
_factory;
[SetUp]
public void SetUp()
{
_factory = new CustomWebApplicationFactory<Program>();
_client = _factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
}
[TearDown]
public void TearDown()
{
_factory.Dispose();
_client.Dispose();
}
MEGJEGYZÉS: A HTTPS-átirányítási figyelmeztetések elkerülése érdekében, amikor a HTTPS-átirányító köztes réteget használja, állítsa be a BaseAddress = new Uri("https://localhost")
Makettszolgáltatások injektálása
A szolgáltatások felülírhatók egy tesztben a ConfigureTestServices hívásával a gazdagép építőn. A felülbírált szolgáltatások konkrét teszt hatóköréhez a WithWebHostBuilder metódus használható egy hostkészítő lekérésére. Ez a következő tesztekben látható:
- Get_QuoteService_ProvidesQuoteInPage
- Szerezd_MegGithubProfilOldalAmiElérhetEgyGithubFelhasználót
- Biztonságos_ElőoldalVisszaadvaHitelesítettFelhasználónak
A minta SUT tartalmaz egy hatókörű szolgáltatást, amely egy idézetet ad vissza. Az indexlap kérésekor az idézet az Index lap rejtett mezőjébe van beágyazva.
Services/IQuoteService.cs
:
public interface IQuoteService
{
Task<string> GenerateQuote();
}
Services/QuoteService.cs
:
// Quote ©1975 BBC: The Doctor (Tom Baker); Dr. Who: Planet of Evil
// https://www.bbc.co.uk/programmes/p00pyrx6
public class QuoteService : IQuoteService
{
public Task<string> GenerateQuote()
{
return Task.FromResult<string>(
"Come on, Sarah. We've an appointment in London, " +
"and we're already 30,000 years late.");
}
}
Program.cs
:
services.AddScoped<IQuoteService, QuoteService>();
Pages/Index.cshtml.cs
:
public class IndexModel : PageModel
{
private readonly ApplicationDbContext _db;
private readonly IQuoteService _quoteService;
public IndexModel(ApplicationDbContext db, IQuoteService quoteService)
{
_db = db;
_quoteService = quoteService;
}
[BindProperty]
public Message Message { get; set; }
public IList<Message> Messages { get; private set; }
[TempData]
public string MessageAnalysisResult { get; set; }
public string Quote { get; private set; }
public async Task OnGetAsync()
{
Messages = await _db.GetMessagesAsync();
Quote = await _quoteService.GenerateQuote();
}
Pages/Index.cs
:
<input id="quote" type="hidden" value="@Model.Quote">
A SUT-alkalmazás futtatásakor a következő korrektúra jön létre:
<input id="quote" type="hidden" value="Come on, Sarah. We've an appointment in
London, and we're already 30,000 years late.">
A szolgáltatás és az injektálás teszteléséhez integrációs tesztben a teszt egy ál-szolgáltatást injektál a SUT-ba. A modellszolgáltatás lecseréli az alkalmazás QuoteService
-t a tesztalkalmazás által biztosított, TestQuoteService
nevű szolgáltatásra.
IntegrationTests.IndexPageTests.cs
:
// Quote ©1975 BBC: The Doctor (Tom Baker); Pyramids of Mars
// https://www.bbc.co.uk/programmes/p00pys55
public class TestQuoteService : IQuoteService
{
public Task<string> GenerateQuote()
{
return Task.FromResult(
"Something's interfering with time, Mr. Scarman, " +
"and time is my business.");
}
}
// Quote ©1975 BBC: The Doctor (Tom Baker); Pyramids of Mars
// https://www.bbc.co.uk/programmes/p00pys55
public class TestQuoteService : IQuoteService
{
public Task<string> GenerateQuote()
{
return Task.FromResult(
"Something's interfering with time, Mr. Scarman, " +
"and time is my business.");
}
}
// Quote ©1975 BBC: The Doctor (Tom Baker); Pyramids of Mars
// https://www.bbc.co.uk/programmes/p00pys55
public class TestQuoteService : IQuoteService
{
public Task<string> GenerateQuote()
{
return Task.FromResult(
"Something's interfering with time, Mr. Scarman, " +
"and time is my business.");
}
}
ConfigureTestServices
van meghívva, és a hatókörön belüli szolgáltatás regisztrálva van:
[Fact]
public async Task Get_QuoteService_ProvidesQuoteInPage()
{
// Arrange
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddScoped<IQuoteService, TestQuoteService>();
});
})
.CreateClient();
//Act
var defaultPage = await client.GetAsync("/");
var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
var quoteElement = content.QuerySelector("#quote");
// Assert
Assert.Equal("Something's interfering with time, Mr. Scarman, " +
"and time is my business.", quoteElement.Attributes["value"].Value);
}
[TestMethod]
public async Task Get_QuoteService_ProvidesQuoteInPage()
{
// Arrange
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddScoped<IQuoteService, TestQuoteService>();
});
})
.CreateClient();
//Act
var defaultPage = await client.GetAsync("/");
var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
var quoteElement = content.QuerySelector("#quote");
// Assert
Assert.AreEqual("Something's interfering with time, Mr. Scarman, " +
"and time is my business.", quoteElement.Attributes["value"].Value);
}
[Test]
public async Task Get_QuoteService_ProvidesQuoteInPage()
{
// Arrange
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddScoped<IQuoteService, TestQuoteService>();
});
})
.CreateClient();
//Act
var defaultPage = await client.GetAsync("/");
var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
var quoteElement = content.QuerySelector("#quote");
// Assert
Assert.That(quoteElement.Attributes["value"].Value, Is.EqualTo(
"Something's interfering with time, Mr. Scarman, " +
"and time is my business."));
}
A teszt végrehajtása során létrehozott jelölések a TestQuoteService
által megadott idézőjelszöveget tükrözik, ezért az állítás sikeres.
<input id="quote" type="hidden" value="Something's interfering with time,
Mr. Scarman, and time is my business.">
Szimulált hitelesítés
Az AuthTests
osztály tesztjei ellenőrzik, hogy egy biztonságos végpont:
- Átirányít egy nem hitelesített felhasználót az alkalmazás bejelentkezési oldalára.
- Egy hitelesített felhasználó tartalmát adja vissza.
A SUT-ban a /SecurePage
lap a AuthorizePage konvenciót használja, hogy egy AuthorizeFilter-t alkalmazzon a lapra. További információkért lásd a Razor oldalak engedélyezési konvencióit.
services.AddRazorPages(options =>
{
options.Conventions.AuthorizePage("/SecurePage");
});
A Get_SecurePageRedirectsAnUnauthenticatedUser
tesztben egy WebApplicationFactoryClientOptions úgy van beállítva, hogy letiltsa az átirányításokat azáltal, hogy AllowAutoRedirect-t false
-ra állítja.
[Fact]
public async Task Get_SecurePageRedirectsAnUnauthenticatedUser()
{
// Arrange
var client = _factory.CreateClient(
new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
// Act
var response = await client.GetAsync("/SecurePage");
// Assert
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.StartsWith("http://localhost/Identity/Account/Login",
response.Headers.Location.OriginalString);
}
[TestMethod]
public async Task Get_SecurePageRedirectsAnUnauthenticatedUser()
{
// Arrange
var client = _factory.CreateClient(
new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
// Act
var response = await client.GetAsync("/SecurePage");
// Assert
Assert.AreEqual(HttpStatusCode.Redirect, response.StatusCode);
StringAssert.StartsWith(response.Headers.Location.OriginalString, "http://localhost/Identity/Account/Login");
}
[Test]
public async Task Get_SecurePageRedirectsAnUnauthenticatedUser()
{
// Arrange
var client = _factory.CreateClient(
new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
// Act
var response = await client.GetAsync("/SecurePage");
// Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Redirect));
Assert.That(response.Headers.Location.OriginalString, Does.StartWith("http://localhost/Identity/Account/Login"));
}
Az ügyfél átirányításának megakadályozásával a következő ellenőrzéseket hajthatja végre:
- A SUT által visszaadott állapotkód a várt HttpStatusCode.Redirect eredményen ellenőrizhető, nem pedig a bejelentkezési lapra való átirányítás utáni végleges állapotkóddal, amely HttpStatusCode.OK.
- A válaszfejlécek közül a
Location
fejléc értékét ellenőrzik annak megállapítására, hogyhttp://localhost/Identity/Account/Login
-el kezdődik-e, nem pedig a végleges bejelentkezési oldal válaszát, ahol aLocation
fejléc nem lenne jelen.
A tesztalkalmazás szimulálhat egy AuthenticationHandler<TOptions>ConfigureTestServices-ben a hitelesítés és az engedélyezés szempontjainak teszteléséhez. Egy minimális forgatókönyv AuthenticateResult.Success-t ad vissza.
public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger, UrlEncoder encoder)
: base(options, logger, encoder)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var claims = new[] { new Claim(ClaimTypes.Name, "Test user") };
var identity = new ClaimsIdentity(claims, "Test");
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, "TestScheme");
var result = AuthenticateResult.Success(ticket);
return Task.FromResult(result);
}
}
public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger, UrlEncoder encoder)
: base(options, logger, encoder)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var claims = new[] { new Claim(ClaimTypes.Name, "Test user") };
var identity = new ClaimsIdentity(claims, "Test");
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, "TestScheme");
var result = AuthenticateResult.Success(ticket);
return Task.FromResult(result);
}
}
public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger, UrlEncoder encoder)
: base(options, logger, encoder)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var claims = new[] { new Claim(ClaimTypes.Name, "Test user") };
var identity = new ClaimsIdentity(claims, "Test");
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, "TestScheme");
var result = AuthenticateResult.Success(ticket);
return Task.FromResult(result);
}
}
A TestAuthHandler
egy felhasználó hitelesítésére hívják meg, amikor a hitelesítési séma TestScheme
van beállítva, ahol a AddAuthentication
regisztrálva van a ConfigureTestServices
-ra. Fontos, hogy a TestScheme
séma megfeleljen az alkalmazás által várt sémának. Ellenkező esetben a hitelesítés nem működik.
[Fact]
public async Task Get_SecurePageIsReturnedForAnAuthenticatedUser()
{
// Arrange
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddAuthentication(defaultScheme: "TestScheme")
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
"TestScheme", options => { });
});
})
.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false,
});
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(scheme: "TestScheme");
//Act
var response = await client.GetAsync("/SecurePage");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
[TestMethod]
public async Task Get_SecurePageIsReturnedForAnAuthenticatedUser()
{
// Arrange
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddAuthentication(defaultScheme: "TestScheme")
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
"TestScheme", options => { });
});
})
.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false,
});
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(scheme: "TestScheme");
//Act
var response = await client.GetAsync("/SecurePage");
// Assert
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
[Test]
public async Task Get_SecurePageIsReturnedForAnAuthenticatedUser()
{
// Arrange
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddAuthentication(defaultScheme: "TestScheme")
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
"TestScheme", options => { });
});
})
.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false,
});
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(scheme: "TestScheme");
//Act
var response = await client.GetAsync("/SecurePage");
// Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
}
A WebApplicationFactoryClientOptions
kapcsolatos további információkért lásd a Ügyfélbeállítások című szakaszt.
A köztes szoftver hitelesítésének alapszintű tesztjei
A Köztes szoftver hitelesítésének alapszintű teszteléséhez tekintse meg ezt a GitHub-adattárat. A tesztforgatókönyvre jellemző tesztkiszolgálót tartalmaz.
A környezet beállítása
Állítsa be a környezetet az egyéni alkalmazás-előállítóban:
public class CustomWebApplicationFactory<TProgram>
: WebApplicationFactory<TProgram> where TProgram : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var dbContextDescriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(IDbContextOptionsConfiguration<ApplicationDbContext>));
services.Remove(dbContextDescriptor);
var dbConnectionDescriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbConnection));
services.Remove(dbConnectionDescriptor);
// Create open SqliteConnection so EF won't automatically close it.
services.AddSingleton<DbConnection>(container =>
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
return connection;
});
services.AddDbContext<ApplicationDbContext>((container, options) =>
{
var connection = container.GetRequiredService<DbConnection>();
options.UseSqlite(connection);
});
});
builder.UseEnvironment("Development");
}
}
public class CustomWebApplicationFactory<TProgram>
: WebApplicationFactory<TProgram> where TProgram : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var dbContextDescriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(IDbContextOptionsConfiguration<ApplicationDbContext>));
services.Remove(dbContextDescriptor);
var dbConnectionDescriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbConnection));
services.Remove(dbConnectionDescriptor);
// Create open SqliteConnection so EF won't automatically close it.
services.AddSingleton<DbConnection>(container =>
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
return connection;
});
services.AddDbContext<ApplicationDbContext>((container, options) =>
{
var connection = container.GetRequiredService<DbConnection>();
options.UseSqlite(connection);
});
});
builder.UseEnvironment("Development");
}
}
public class CustomWebApplicationFactory<TProgram>
: WebApplicationFactory<TProgram> where TProgram : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var dbContextDescriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(IDbContextOptionsConfiguration<ApplicationDbContext>));
services.Remove(dbContextDescriptor);
var dbConnectionDescriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbConnection));
services.Remove(dbConnectionDescriptor);
// Create open SqliteConnection so EF won't automatically close it.
services.AddSingleton<DbConnection>(container =>
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
return connection;
});
services.AddDbContext<ApplicationDbContext>((container, options) =>
{
var connection = container.GetRequiredService<DbConnection>();
options.UseSqlite(connection);
});
});
builder.UseEnvironment("Development");
}
}
Hogyan következtet a tesztinfrastruktúra az alkalmazás tartalomgyökerének elérési útjára?
A WebApplicationFactory
konstruktor meghatározza az alkalmazás tartalom gyökér elérési útját úgy, hogy a WebApplicationFactoryContentRootAttribute szerelvény TEntryPoint
kulccsal a System.Reflection.Assembly.FullName
-t keres az integrációs teszteket tartalmazó szerelvényen. Ha nem található a megfelelő kulccsal rendelkező attribútum, WebApplicationFactory
visszatér a megoldásfájl (.sln) kereséséhez, és hozzáadja a TEntryPoint
szerelvény nevét a megoldáskönyvtárhoz. Az alkalmazás gyökérkönyvtára (a tartalom gyökérútvonala) a nézetek és tartalomfájlok felderítésére szolgál.
Árnyékmásolás letiltása
Az árnyékmásolás miatt a tesztek a kimeneti könyvtártól eltérő könyvtárban futnak. Ha a tesztek a fájlok Assembly.Location
-hoz viszonyított betöltésére támaszkodnak, és problémákat tapasztalunk, előfordulhat, hogy le kell tiltani az árnyékmásolást.
Ha le szeretné tiltani az árnyékmásolást az xUnit használatakor, hozzon létre egy xunit.runner.json
fájlt a tesztprojekt könyvtárában a megfelelő konfigurációs beállítással:
{
"shadowCopy": false
}
Objektumok ártalmatlanítása
A IClassFixture
implementáció tesztjeinek végrehajtása után TestServer és HttpClient megsemmisülnek, amikor az xUnit megsemmisíti a WebApplicationFactory
-at. Ha a fejlesztő által példányosított objektumok ártalmatlanítást igényelnek, azokat a IClassFixture
implementációban kell megsemmisíteni. További információért lásd: Dispose metódus implementálása.
A TestClass
tesztjeinek végrehajtása után az MSTest a TestServer metódusban megsemmisíti a HttpClient-et, ekkor pedig a WebApplicationFactory
és ClassCleanup
is megsemmisülnek. Ha a fejlesztő által példányosított objektumok megsemmisítést igényelnek, a ClassCleanup
metódusban kell megsemmisíteni őket. További információért lásd: Dispose metódus implementálása.
A tesztosztály tesztjeinek végrehajtása után az NUnit megsemmisíti a TestServer a HttpClient metódusban. Ha a fejlesztő által példányosított objektumok megsemmisítést igényelnek, a TearDown
metódusban kell megsemmisíteni őket. További információért lásd: Dispose metódus implementálása.
Integrációs tesztek mintája
A mintaalkalmazás két alkalmazásból áll:
Alkalmazás | Projektmappa | Leírás |
---|---|---|
Üzenetküldő alkalmazás (a SUT) | src/RazorPagesProject |
Lehetővé teszi a felhasználó számára, hogy hozzáadjon, töröljön egyet, törölje az összeset, és elemezze az üzeneteket. |
Alkalmazás tesztelése | tests/RazorPagesProject.Tests |
A SUT integrációs tesztelésére szolgál. |
A tesztek egy IDE beépített tesztfunkcióival futtathatók, például Visual Studio. Ha Visual Studio Code vagy parancssort használ, hajtsa végre a következő parancsot egy parancssorban a tests/RazorPagesProject.Tests
könyvtárban:
dotnet test
Üzenetküldő alkalmazás (SUT) szervezete
A SUT egy Razor Pages üzenetrendszer, amely a következő jellemzőkkel rendelkezik:
- Az alkalmazás indexoldala (
Pages/Index.cshtml
ésPages/Index.cshtml.cs
) felhasználói felületi és lapmodellezési módszereket biztosít az üzenetek hozzáadásának, törlésének és elemzésének szabályozásához (átlagos szavak üzenetenként). - Az üzeneteket a
Message
osztály (Data/Message.cs
) írja le két tulajdonságokkal:Id
(kulcs) ésText
(üzenet). AText
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 egy adatelérési réteget (DAL) tartalmaz az adatbázis környezeti osztályában,
AppDbContext
(Data/AppDbContext.cs
). - Ha az adatbázis üres az alkalmazás indításakor, az üzenettároló három üzenettel lesz inicializálva.
- Az alkalmazás tartalmaz egy
/SecurePage
, amelyet csak hitelesített felhasználó érhet el.
†Az EF-cikk, Az InMemorytesztelése című cikk 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 az alkalmazás nem használja az adattár mintát, és nem hatékony példa a Munkaegység (UoW) mintára, a Razor Pages támogatja ezeket a fejlesztési mintákat. További információ: Az infrastruktúra perzisztenciaréteg tervezése és Tesztvezérlő logika (a minta implementálja az adattár mintát).
Tesztalkalmazás szervezése
A tesztalkalmazás egy konzolalkalmazás a tests/RazorPagesProject.Tests
könyvtárban.
Alkalmazáskönyvtár tesztelése | Leírás |
---|---|
AuthTests |
A következő tesztelési módszereket tartalmazza:
|
BasicTests |
Az útválasztás és a tartalomtípus tesztelési módszerét tartalmazza. |
IntegrationTests |
Az Index lap integrációs tesztjeit tartalmazza egyéni WebApplicationFactory osztály használatával. |
Helpers/Utilities |
|
A tesztelési keretrendszer xUnit. Az integrációs teszteket a Microsoft.AspNetCore.TestHostsegítségével hajtják végre, amely magában foglalja a TestServer-et. Mivel a Microsoft.AspNetCore.Mvc.Testing
csomag a tesztgazda és a tesztkiszolgáló konfigurálására szolgál, a TestHost
és TestServer
csomagok nem igényelnek közvetlen csomaghivatkozásokat a tesztalkalmazás projektfájljában vagy fejlesztői konfigurációjában a tesztalkalmazásban.
Az integrációs tesztek általában kis adatkészletet igényelnek az adatbázisban a teszt végrehajtása előtt. A törlési teszt például egy adatbázisrekord törlését kéri, ezért az adatbázisnak legalább egy rekorddal kell rendelkeznie ahhoz, hogy a törlési kérelem sikeres legyen.
A mintaalkalmazás három üzenettel tölti fel az adatbázist a Utilities.cs
-ben, amelyeket a tesztek futtatásakor használhatnak.
public static void InitializeDbForTests(ApplicationDbContext db)
{
db.Messages.AddRange(GetSeedingMessages());
db.SaveChanges();
}
public static void ReinitializeDbForTests(ApplicationDbContext db)
{
db.Messages.RemoveRange(db.Messages);
InitializeDbForTests(db);
}
public static List<Message> GetSeedingMessages()
{
return new List<Message>()
{
new Message(){ Text = "TEST RECORD: You're standing on my scarf." },
new Message(){ Text = "TEST RECORD: Would you like a jelly baby?" },
new Message(){ Text = "TEST RECORD: To the rational mind, " +
"nothing is inexplicable; only unexplained." }
};
}
A SUT adatbázis-környezetét regisztrálták Program.cs
-ben. A tesztalkalmazás builder.ConfigureServices
visszahívása az alkalmazás lesz végrehajtva. Ha másik adatbázist szeretne használni a tesztekhez, az alkalmazás adatbázis-környezetét le kell cserélni a builder.ConfigureServices
-ban. A további információkért lásd a WebApplicationFactory testreszabása szakaszt.
További erőforrások
- egységtesztek
- Razor Lapok egységtesztjei ASP.NET Core
- ASP.NET Core köztes szoftver
- Tesztvezérlő logika az ASP.NET Core-ban
- Köztes szoftver hitelesítésének alapszintű tesztjei
Ez a témakör feltételezi az egységtesztek alapszintű megértését. Ha nem ismeri a tesztelési fogalmakat, tekintse meg a .NET Core és a .NET Standard Egységtesztelés című témakört és annak csatolt tartalmát.
Mintakód megtekintése vagy letöltése (hogyan kell letölteni)
A mintaalkalmazás egy Razor Pages-alkalmazás, és feltételezi, hogy alapszintű ismeretekkel rendelkezik a Razor Pages-ről. Ha nem ismeri Razor oldalakat, tekintse meg az alábbi témaköröket:
Jegyzet
Az SLA-k teszteléséhez olyan eszközt ajánlunk, mint a Playwright for .NET, amely automatizálhatja a böngészőt.
Bevezetés az integrációs tesztekbe
Az integrációs tesztek az alkalmazás összetevőit szélesebb szinten értékelik ki, mint egységtesztek. Az egységtesztek izolált szoftverösszetevők, például az egyes osztálymódszerek tesztelésére szolgálnak. Az integrációs tesztek megerősítik, hogy két vagy több alkalmazásösszetevő együttműködik a várt eredmény érdekében, beleértve a kérések teljes feldolgozásához szükséges összes összetevőt is.
Ezek a szélesebb körű tesztek az alkalmazás infrastruktúrájának és teljes keretrendszerének tesztelésére szolgálnak, gyakran a következő összetevőket is beleértve:
- Adatbázis
- Fájlrendszer
- Hálózati berendezések
- Kérelem-válasz folyamat
Az egységtesztek az infrastruktúra-összetevők helyett létrehozott összetevőket használnak, más néven hamis vagy szimulált objektumokat.
Az egységtesztekkel ellentétben az integrációs tesztek:
- Használja azokat a tényleges összetevőket, amelyeket az alkalmazás éles környezetben használ.
- További kód- és adatfeldolgozást igényel.
- A futtatás hosszabb időt vesz igénybe.
Ezért az integrációs tesztek használatának korlátozása a legfontosabb infrastruktúra-forgatókönyvekre. Ha egy viselkedést egységteszt vagy integrációs teszt használatával lehet tesztelni, válassza ki az egységtesztet.
Az integrációs tesztek megvitatása során a tesztelt projektet gyakran nevezik System Under Test, vagy röviden "SUT" néven. A cikk során a "SUT" a tesztelt ASP.NET Core-alkalmazásra hivatkozik.
Ne írjon integrációs teszteket az adatbázisok és fájlrendszerek minden permutációs adat- és fájlhozzáféréshez. Függetlenül attól, hogy az alkalmazások hány helyen használják az adatbázisokat és a fájlrendszereket, az olvasási, írási, frissítési és törlési integrációs tesztek koncentrált készlete általában képes megfelelően tesztelni az adatbázis- és fájlrendszer-összetevőket. Használjon egységteszteket az ezen összetevőkkel interakcióba lépő metóduslogika rutintesztjeihez. Az egységtesztekben az infrastruktúra hamis vagy hamisított használata gyorsabb tesztvégrehajtást eredményez.
ASP.NET Core integrációs tesztek
A ASP.NET Core integrációs tesztjeihez a következők szükségesek:
- A tesztprojektek a teszteket tartalmazzák és hajtják végre. A tesztprojekt hivatkozással rendelkezik a SUT-ra.
- A tesztprojekt létrehoz egy tesztwebhely-gazdagépet a SUT számára, és egy tesztkiszolgáló-ügyféllel kezeli a kérelmeket és a válaszokat a SUT-val.
- A tesztfuttató a tesztek végrehajtására és a teszteredmények jelentésére szolgál.
Az integrációs tesztek olyan események sorozatát követik, amelyek magukban foglalják a szokásos Előkészület, Végrehajtásés Ellenőrzés tesztelési lépéseket.
- Az SUT webhoszt konfigurálva van.
- Létrejön egy tesztkiszolgáló-ügyfél, amely kéréseket küld az alkalmazásnak.
- A Arrange tesztelési lépés végrehajtása: A tesztalkalmazás előkészít egy kérést.
- A törvény tesztlépés végrehajtása: Az ügyfél elküldi a kérést, és megkapja a választ.
- A ellenőrzési tesztlépést végrehajtják: A tényleges választ a rendszer a várt válasz alapján érvényesíti, és érvényesnek nyilvánítja (), vagy sikertelennek ().
- A folyamat az összes teszt végrehajtásáig folytatódik.
- A teszteredmények jelentésre kerültek.
A teszt webgazda általában eltérő konfigurációval van beállítva, mint az alkalmazás normál webgazdája a tesztek futtatásához. Előfordulhat például, hogy a tesztekhez egy másik adatbázist vagy különböző alkalmazásbeállításokat használnak.
Az infrastruktúra-összetevőket, például a tesztweb-gazdagépet és a memóriabeli tesztkiszolgálót (TestServer) a Microsoft.AspNetCore.Mvc.Testing csomag biztosítja vagy felügyeli. A csomag használata leegyszerűsíti a tesztek létrehozását és végrehajtását.
A Microsoft.AspNetCore.Mvc.Testing
csomag a következő feladatokat kezeli:
- Másolja a függőségi fájlt (
.deps
) a SUT-ból a tesztprojektbin
könyvtárába. - A tartalomgyökerét-re a SUT projektgyökeréhez állítja, hogy a statikus fájlok és a lapok/nézetek megtalálhatók legyenek a tesztek végrehajtásakor.
- A WebApplicationFactory osztály gördülékenyebbé teszi a SUT
TestServer
használatával történő rendszerindítását.
Az egységtesztek dokumentációja ismerteti a tesztprojektek és tesztfuttatók beállításának módját, valamint a tesztek futtatásának részletes utasításait, valamint a tesztek és tesztosztályok elnevezésére vonatkozó javaslatokat.
Egységtesztek elkülönítése az integrációs tesztektől különböző projektekre. A tesztek elkülönítése:
- Segít biztosítani, hogy az infrastruktúra tesztelési összetevői véletlenül ne legyenek belefoglalva az egységtesztekbe.
- Lehetővé teszi a tesztkészlet futtatásának szabályozását.
Gyakorlatilag nincs különbség a Razor Pages-alkalmazások és az MVC-alkalmazások tesztjeinek konfigurációja között. Az egyetlen különbség a tesztek elnevezésében van. Egy Razor Pages-alkalmazásban az oldalvégpontok tesztjeit általában az oldalmodell-osztályról nevezik el (például IndexPageTests
az indexlap összetevőintegrációjának teszteléséhez). Az MVC-alkalmazásokban a tesztek általában vezérlőosztályok szerint vannak rendszerezve, és az általuk tesztelt vezérlőkről nevezik el őket (például HomeControllerTests
a Home vezérlő összetevőintegrációjának teszteléséhez).
Alkalmazás előfeltételeinek tesztelése
A tesztprojektnek a következőnek kell lennie:
- Hivatkozzon a
Microsoft.AspNetCore.Mvc.Testing
csomagra. - Adja meg a webes SDK-t a projektfájlban (
<Project Sdk="Microsoft.NET.Sdk.Web">
).
Ezek az előfeltételek láthatók a mintaalkalmazás-ban. Vizsgálja meg a tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj
fájlt. A mintaalkalmazás az xUnit tesztelési keretrendszert és a AngleSharp elemzőtárat használja, így a mintaalkalmazás a következőre is hivatkozik:
A xunit.runner.visualstudio
2.4.2-es vagy újabb verzióját használó alkalmazásokban a tesztprojektnek hivatkoznia kell a Microsoft.NET.Test.Sdk
csomagra.
Az Entity Framework Core a tesztekben is használatos. Az alkalmazás hivatkozásai:
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
Microsoft.AspNetCore.Identity.EntityFrameworkCore
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.InMemory
Microsoft.EntityFrameworkCore.Tools
SUT-környezet
Ha a SUT környezet nincs beállítva, a környezet alapértelmezés szerint a Fejlesztési környezet.
Alapszintű tesztek az alapértelmezett WebApplicationFactory használatával
A WebApplicationFactory<TEntryPoint>-t használjuk egy TestServer létrehozásához az integrációs tesztekhez.
TEntryPoint
a SUT belépési pontosztálya, általában a Startup
osztály.
A tesztosztályok implementálnak egy osztály-fix technikai felületet (IClassFixture
), amely jelzi, hogy az osztály teszteket tartalmaz, és megosztott objektumpéldányokat biztosít az osztály tesztjei során.
Az alábbi tesztosztály BasicTests
a WebApplicationFactory
használatával végzi a SUT inicializálását, és biztosít egy HttpClient-t a Get_EndpointsReturnSuccessAndCorrectContentType
tesztmetódus számára. A metódus ellenőrzi, hogy a válasz állapotkódja sikeres-e (az állapotkódok a 200–299 tartományba esnek), és hogy a Content-Type
fejléc több alkalmazásoldal esetében text/html; charset=utf-8
-e.
CreateClient() létrehoz egy HttpClient
-példányt, amely automatikusan követi az átirányításokat és kezeli a cookie-kat.
public class BasicTests
: IClassFixture<WebApplicationFactory<RazorPagesProject.Startup>>
{
private readonly WebApplicationFactory<RazorPagesProject.Startup> _factory;
public BasicTests(WebApplicationFactory<RazorPagesProject.Startup> factory)
{
_factory = factory;
}
[Theory]
[InlineData("/")]
[InlineData("/Index")]
[InlineData("/About")]
[InlineData("/Privacy")]
[InlineData("/Contact")]
public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.Equal("text/html; charset=utf-8",
response.Content.Headers.ContentType.ToString());
}
}
Alapértelmezés szerint a nem alapvető cookie-k nem maradnak meg a kérések között, ha engedélyezve van a GDPR hozzájárulási szabályzata. A nem alapvető cookie-k( például a TempData-szolgáltató által használt) megőrzése érdekében jelölje meg őket alapvetőként a tesztek során. A cookie alapvetőként való megjelölésére vonatkozó utasításokért lásd: Alapvető cookie-k.
A WebApplicationFactory testreszabása
A webtárhely konfigurációja a tesztosztályoktól függetlenül hozható létre, úgy, hogy a WebApplicationFactory
-ból örökölt egy vagy több egyéni gyárat készítünk.
Örököljön
WebApplicationFactory
és bírálja felül ConfigureWebHost. A IWebHostBuilder lehetővé teszi a szolgáltatások gyűjteményének konfigurálását a ConfigureServicessegítségével.public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup: class { protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureServices(services => { var descriptor = services.SingleOrDefault( d => d.ServiceType == typeof(DbContextOptions<ApplicationDbContext>)); services.Remove(descriptor); services.AddDbContext<ApplicationDbContext>(options => { options.UseInMemoryDatabase("InMemoryDbForTesting"); }); var sp = services.BuildServiceProvider(); using (var scope = sp.CreateScope()) { var scopedServices = scope.ServiceProvider; var db = scopedServices.GetRequiredService<ApplicationDbContext>(); var logger = scopedServices .GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>(); db.Database.EnsureCreated(); try { Utilities.InitializeDbForTests(db); } catch (Exception ex) { logger.LogError(ex, "An error occurred seeding the " + "database with test messages. Error: {Message}", ex.Message); } } }); } }
Az adatbázis-vetést a mintaalkalmazásban a
InitializeDbForTests
metódus végzi. A módszert az integrációs tesztek mintájában ismertetjük: Az alkalmazás szervezetének tesztelése szakasz.A SUT adatbázis-környezete a
Startup.ConfigureServices
metódusában van regisztrálva. A tesztalkalmazásbuilder.ConfigureServices
visszahívása az alkalmazás lesz végrehajtva. A végrehajtási sorrend a Általános gazdagép a ASP.NET Core 3.0 kiadásával történő kompatibilitástörő változás. Ha más adatbázist szeretne használni a tesztekhez, mint az alkalmazás adatbázisa, az alkalmazás adatbázis-környezetét abuilder.ConfigureServices
adatbázis-környezetre kell cserélni.Azokban az esetekben, amikor a SUT-ok továbbra is a Web Host-ot használják, a tesztalkalmazás
builder.ConfigureServices
visszahívása még a SUTStartup.ConfigureServices
kódja előtt kerül végrehajtásra. A tesztalkalmazásbuilder.ConfigureTestServices
visszahívása után lesz végrehajtva.A mintaalkalmazás megkeresi az adatbázis-környezet szolgáltatásleíróját, és a leíró használatával eltávolítja a szolgáltatásregisztrációt. Ezután a gyár hozzáad egy új
ApplicationDbContext
, amely memórián belüli adatbázist használ a tesztekhez.Ha a memóriában lévő adatbázistól eltérő adatbázishoz szeretne csatlakozni, módosítsa a
UseInMemoryDatabase
hívást a környezet másik adatbázishoz való csatlakoztatásához. SQL Server tesztadatbázis használata:- A projektfájlban hivatkozzon a
Microsoft.EntityFrameworkCore.SqlServer
NuGet-csomagra. - Hívja meg
UseSqlServer
egy kapcsolati sztringgel az adatbázishoz.
services.AddDbContext<ApplicationDbContext>((options, context) => { context.UseSqlServer( Configuration.GetConnectionString("TestingDbConnectionString")); });
- A projektfájlban hivatkozzon a
Az egyéni
CustomWebApplicationFactory
-t használja a tesztosztályokban. Az alábbi példa a gyárat használja aIndexPageTests
osztályban:public class IndexPageTests : IClassFixture<CustomWebApplicationFactory<RazorPagesProject.Startup>> { private readonly HttpClient _client; private readonly CustomWebApplicationFactory<RazorPagesProject.Startup> _factory; public IndexPageTests( CustomWebApplicationFactory<RazorPagesProject.Startup> factory) { _factory = factory; _client = factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false }); }
A mintaalkalmazás ügyfele úgy van konfigurálva, hogy megakadályozza a
HttpClient
átirányítások követését. Amint azt a Mock authentication szakasz későbbi szakasza ismerteti, ez lehetővé teszi a tesztek számára az alkalmazás első válaszának eredményének ellenőrzését. Az első válasz ezek közül sok tesztben egy átirányítás egy "Location
" fejléccel.Egy tipikus teszt a
HttpClient
és segédmetenek használatával dolgozza fel a kérést és a választ:[Fact] public async Task Post_DeleteAllMessagesHandler_ReturnsRedirectToRoot() { // Arrange var defaultPage = await _client.GetAsync("/"); var content = await HtmlHelpers.GetDocumentAsync(defaultPage); //Act var response = await _client.SendAsync( (IHtmlFormElement)content.QuerySelector("form[id='messages']"), (IHtmlButtonElement)content.QuerySelector("button[id='deleteAllBtn']")); // Assert Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode); Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); Assert.Equal("/", response.Headers.Location.OriginalString); }
A SUT-nak küldött POST-kéréseknek meg kell felelniük az alkalmazás adatvédelmi rendszerének automatikusan végrehajtott hamisítás elleni ellenőrzési folyamatának. A teszt POST-kérésének rendezéséhez a tesztalkalmazásnak a következőnek kell lennie:
- Kérjen egy kérelmet az oldalhoz.
- Elemezze az azonosítóhamisítás elleni cookie-t, és kérje le az érvényesítési jogkivonatot a választól.
- Végezze el a POST kérést az antiforgery cookie és az érvényesítési jogkivonattal.
A mintaalkalmazás SendAsync
segédmetódusai (Helpers/HttpClientExtensions.cs
) és a GetDocumentAsync
segédmetódus (Helpers/HtmlHelpers.cs
) az AngleSharp elemzővel kezelik az antiforgery-ellenőrzést az alábbi módszerekkel:
-
GetDocumentAsync
: Megkapja a HttpResponseMessage, és visszaad egyIHtmlDocument
.GetDocumentAsync
egy olyan gyárat használ, amely az eredeti alapján készít elő egyHttpResponseMessage
-t. További információért tekintse meg az AngleSharp dokumentációt. -
SendAsync
kiterjesztési metódusai aHttpClient
összállítanak egy HttpRequestMessage-t, és meghívják a SendAsync(HttpRequestMessage)-t, hogy kérelmeket küldjenek a SUT-nak. ASendAsync
túlterhelései elfogadják a HTML-űrlapot (IHtmlFormElement
) és a következőket:- Az űrlap Küldés gombja (
IHtmlElement
) - Űrlapértékek gyűjtése (
IEnumerable<KeyValuePair<string, string>>
) - Küldés gomb (
IHtmlElement
) és űrlapértékek (IEnumerable<KeyValuePair<string, string>>
)
- Az űrlap Küldés gombja (
Jegyzet
AngleSharp egy harmadik féltől származó elemzési kódtár, amelyet bemutató célokra használnak ebben a témakörben és a mintaalkalmazásban. Az AngleSharp nem támogatott vagy szükséges ASP.NET Core-alkalmazások integrációs teszteléséhez. Más elemzők is használhatók, például a Html Agility Pack (HAP). Egy másik módszer, ha olyan kódot írunk, amely közvetlenül kezeli az antiforgery rendszer kérés-ellenőrzési tokenjét és az antiforgery cookie-t.
Jegyzet
A EF-Core memóriabeli adatbázis-szolgáltató korlátozott és alapszintű teszteléshez használható, de a memóriabeli teszteléshez ajánlott SQLite-szolgáltató.
Az ügyfél testreszabása a WithWebHostBuilder használatával
Ha egy tesztmetóduson belül további konfigurációra van szükség, WithWebHostBuilder létrehoz egy új WebApplicationFactory
egy olyan IWebHostBuilder-vel, amelyet a konfiguráció további testreszab.
A mintaalkalmazás Post_DeleteMessageHandler_ReturnsRedirectToRoot
tesztmetódusa a WithWebHostBuilder
használatát mutatja be. Ez a teszt egy rekord törlését hajtja végre az adatbázisban egy űrlapbeküldés SUT-ban való aktiválásával.
Mivel a IndexPageTests
osztály egy másik tesztje olyan műveletet hajt végre, amely törli az adatbázis összes rekordját, és a Post_DeleteMessageHandler_ReturnsRedirectToRoot
metódus előtt futhat, az adatbázist ebben a tesztmetódusban újból el kell végezni, hogy a rendszer egy rekordot adjon meg a SUT számára a törléshez. A SUT-ban a messages
űrlap első törlési gombjának kiválasztását a rendszer szimulálja a SUT-nak küldött kérésben:
[Fact]
public async Task Post_DeleteMessageHandler_ReturnsRedirectToRoot()
{
// Arrange
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
var serviceProvider = services.BuildServiceProvider();
using (var scope = serviceProvider.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices
.GetRequiredService<ApplicationDbContext>();
var logger = scopedServices
.GetRequiredService<ILogger<IndexPageTests>>();
try
{
Utilities.ReinitializeDbForTests(db);
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred seeding " +
"the database with test messages. Error: {Message}",
ex.Message);
}
}
});
})
.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
var defaultPage = await client.GetAsync("/");
var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
//Act
var response = await client.SendAsync(
(IHtmlFormElement)content.QuerySelector("form[id='messages']"),
(IHtmlButtonElement)content.QuerySelector("form[id='messages']")
.QuerySelector("div[class='panel-body']")
.QuerySelector("button"));
// Assert
Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode);
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.Equal("/", response.Headers.Location.OriginalString);
}
Ügyfélbeállítások
Az alábbi táblázat a WebApplicationFactoryClientOptions-példányok létrehozásakor elérhető alapértelmezett HttpClient
mutatja.
Opció | Leírás | Alapértelmezett |
---|---|---|
AllowAutoRedirect | Lekérdezi vagy beállítja, hogy HttpClient példányoknak automatikusan követnie kell-e az átirányítási válaszokat. |
true |
BaseAddress | Lekéri vagy beállítja HttpClient példányok alapcímét. |
http://localhost |
HandleCookies | Lekérdezi vagy beállítja, hogy HttpClient példányok kezeljenek-e cookie-kat. |
true |
MaxAutomaticRedirections | Lekéri vagy beállítja a maximális számát azoknak az átirányítási válaszoknak, amelyeket a HttpClient példányoknak követniük kell. |
7 |
Hozza létre a WebApplicationFactoryClientOptions
osztályt, és adja át a CreateClient() metódusnak (az alapértelmezett értékek a példakódban láthatók):
// Default client option values are shown
var clientOptions = new WebApplicationFactoryClientOptions();
clientOptions.AllowAutoRedirect = true;
clientOptions.BaseAddress = new Uri("http://localhost");
clientOptions.HandleCookies = true;
clientOptions.MaxAutomaticRedirections = 7;
_client = _factory.CreateClient(clientOptions);
Makettszolgáltatások injektálása
A szolgáltatások felülírhatók egy tesztben a ConfigureTestServices hívásával a gazdagép építőn.
A modellszolgáltatások injektálásához a SUT-nak rendelkeznie kell egy Startup
osztállyal, amely tartalmaz egy Startup.ConfigureServices
metódust.
A minta SUT tartalmaz egy hatókörű szolgáltatást, amely egy idézetet ad vissza. Az indexlap kérésekor az idézet az Index lap rejtett mezőjébe van beágyazva.
Services/IQuoteService.cs
:
public interface IQuoteService
{
Task<string> GenerateQuote();
}
Services/QuoteService.cs
:
// Quote ©1975 BBC: The Doctor (Tom Baker); Dr. Who: Planet of Evil
// https://www.bbc.co.uk/programmes/p00pyrx6
public class QuoteService : IQuoteService
{
public Task<string> GenerateQuote()
{
return Task.FromResult<string>(
"Come on, Sarah. We've an appointment in London, " +
"and we're already 30,000 years late.");
}
}
Startup.cs
:
services.AddScoped<IQuoteService, QuoteService>();
Pages/Index.cshtml.cs
:
public class IndexModel : PageModel
{
private readonly ApplicationDbContext _db;
private readonly IQuoteService _quoteService;
public IndexModel(ApplicationDbContext db, IQuoteService quoteService)
{
_db = db;
_quoteService = quoteService;
}
[BindProperty]
public Message Message { get; set; }
public IList<Message> Messages { get; private set; }
[TempData]
public string MessageAnalysisResult { get; set; }
public string Quote { get; private set; }
public async Task OnGetAsync()
{
Messages = await _db.GetMessagesAsync();
Quote = await _quoteService.GenerateQuote();
}
Pages/Index.cs
:
<input id="quote" type="hidden" value="@Model.Quote">
A SUT-alkalmazás futtatásakor a következő korrektúra jön létre:
<input id="quote" type="hidden" value="Come on, Sarah. We've an appointment in
London, and we're already 30,000 years late.">
A szolgáltatás és az injektálás teszteléséhez integrációs tesztben a teszt egy ál-szolgáltatást injektál a SUT-ba. A modellszolgáltatás lecseréli az alkalmazás QuoteService
-t a tesztalkalmazás által biztosított, TestQuoteService
nevű szolgáltatásra.
IntegrationTests.IndexPageTests.cs
:
// Quote ©1975 BBC: The Doctor (Tom Baker); Pyramids of Mars
// https://www.bbc.co.uk/programmes/p00pys55
public class TestQuoteService : IQuoteService
{
public Task<string> GenerateQuote()
{
return Task.FromResult<string>(
"Something's interfering with time, Mr. Scarman, " +
"and time is my business.");
}
}
ConfigureTestServices
van meghívva, és a hatókörön belüli szolgáltatás regisztrálva van:
[Fact]
public async Task Get_QuoteService_ProvidesQuoteInPage()
{
// Arrange
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddScoped<IQuoteService, TestQuoteService>();
});
})
.CreateClient();
//Act
var defaultPage = await client.GetAsync("/");
var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
var quoteElement = content.QuerySelector("#quote");
// Assert
Assert.Equal("Something's interfering with time, Mr. Scarman, " +
"and time is my business.", quoteElement.Attributes["value"].Value);
}
A teszt végrehajtása során létrehozott jelölések a TestQuoteService
által megadott idézőjelszöveget tükrözik, ezért az állítás sikeres.
<input id="quote" type="hidden" value="Something's interfering with time,
Mr. Scarman, and time is my business.">
Szimulált hitelesítés
Az AuthTests
osztály tesztjei ellenőrzik, hogy egy biztonságos végpont:
- Átirányít egy nem hitelesített felhasználót az alkalmazás Bejelentkezési lapjára.
- Egy hitelesített felhasználó tartalmát adja vissza.
A SUT-ban a /SecurePage
lap a AuthorizePage konvenciót használja, hogy egy AuthorizeFilter-t alkalmazzon a lapra. További információkért lásd a Razor oldalak engedélyezési konvencióit.
services.AddRazorPages(options =>
{
options.Conventions.AuthorizePage("/SecurePage");
});
A Get_SecurePageRedirectsAnUnauthenticatedUser
tesztben egy WebApplicationFactoryClientOptions úgy van beállítva, hogy letiltsa az átirányításokat azáltal, hogy AllowAutoRedirect-t false
-ra állítja.
[Fact]
public async Task Get_SecurePageRedirectsAnUnauthenticatedUser()
{
// Arrange
var client = _factory.CreateClient(
new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
// Act
var response = await client.GetAsync("/SecurePage");
// Assert
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.StartsWith("http://localhost/Identity/Account/Login",
response.Headers.Location.OriginalString);
}
Az ügyfél átirányításának megakadályozásával a következő ellenőrzéseket hajthatja végre:
- A SUT által visszaadott állapotkódot a várt HttpStatusCode.Redirect eredmény alapján lehet ellenőrizni, nem pedig a bejelentkezési lapra való átirányítás utáni végleges állapotkódot, amely HttpStatusCode.OK.
- A válaszfejlécek
Location
fejlécértékét ellenőrizni kell, hogy azhttp://localhost/Identity/Account/Login
-gyel kezdődik-e, nem pedig a bejelentkezési oldal végső válasza helyett, ahol aLocation
fejléc nem lenne jelen.
A tesztalkalmazás szimulálhat egy AuthenticationHandler<TOptions>ConfigureTestServices-ben a hitelesítés és az engedélyezés szempontjainak teszteléséhez. Egy minimális forgatókönyv AuthenticateResult.Success-t ad vissza.
public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var claims = new[] { new Claim(ClaimTypes.Name, "Test user") };
var identity = new ClaimsIdentity(claims, "Test");
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, "Test");
var result = AuthenticateResult.Success(ticket);
return Task.FromResult(result);
}
}
A TestAuthHandler
egy felhasználó hitelesítésére hívják meg, amikor a hitelesítési séma Test
van beállítva, ahol a AddAuthentication
regisztrálva van a ConfigureTestServices
-ra. Fontos, hogy a Test
séma megfeleljen az alkalmazás által várt sémának. Ellenkező esetben a hitelesítés nem működik.
[Fact]
public async Task Get_SecurePageIsReturnedForAnAuthenticatedUser()
{
// Arrange
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddAuthentication("Test")
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
"Test", options => {});
});
})
.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false,
});
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Test");
//Act
var response = await client.GetAsync("/SecurePage");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
A WebApplicationFactoryClientOptions
kapcsolatos további információkért lásd a Ügyfélbeállítások című szakaszt.
A környezet beállítása
Alapértelmezés szerint az SUT gazdagép- és alkalmazáskörnyezete a fejlesztési környezet használatára van konfigurálva. A SUT környezetének felülírása a IHostBuilder
használatakor:
- Állítsa be a
ASPNETCORE_ENVIRONMENT
környezeti változót (példáulStaging
,Production
vagy más egyéni értéket, példáulTesting
). - Felülbírálja a(z)
CreateHostBuilder
-t a tesztalkalmazásban aASPNETCORE
előtagú környezeti változók olvasásához.
protected override IHostBuilder CreateHostBuilder() =>
base.CreateHostBuilder()
.ConfigureHostConfiguration(
config => config.AddEnvironmentVariables("ASPNETCORE"));
Ha a SUT a webszervert (IWebHostBuilder
) használja, felülírja a CreateWebHostBuilder
-t:
protected override IWebHostBuilder CreateWebHostBuilder() =>
base.CreateWebHostBuilder().UseEnvironment("Testing");
Hogyan következtet a tesztinfrastruktúra az alkalmazás tartalomgyökerének elérési útjára?
A WebApplicationFactory
konstruktor meghatározza az alkalmazás tartalom gyökér elérési útját úgy, hogy a WebApplicationFactoryContentRootAttribute szerelvény TEntryPoint
kulccsal a System.Reflection.Assembly.FullName
-t keres az integrációs teszteket tartalmazó szerelvényen. Ha nem található a megfelelő kulccsal rendelkező attribútum, WebApplicationFactory
visszatér a megoldásfájl (.sln) kereséséhez, és hozzáadja a TEntryPoint
szerelvény nevét a megoldáskönyvtárhoz. Az alkalmazás gyökérkönyvtára (a tartalom gyökérútvonala) a nézetek és tartalomfájlok felderítésére szolgál.
Árnyékmásolás letiltása
Az árnyékmásolás miatt a tesztek a kimeneti könyvtártól eltérő könyvtárban futnak. Ha a tesztek a fájlok Assembly.Location
-hoz viszonyított betöltésére támaszkodnak, és problémákat tapasztalunk, előfordulhat, hogy le kell tiltani az árnyékmásolást.
Ha le szeretné tiltani az árnyékmásolást az xUnit használatakor, hozzon létre egy xunit.runner.json
fájlt a tesztprojekt könyvtárában a megfelelő konfigurációs beállítással:
{
"shadowCopy": false
}
Objektumok ártalmatlanítása
A IClassFixture
implementáció tesztjeinek végrehajtása után TestServer és HttpClient megsemmisülnek, amikor az xUnit megsemmisíti a WebApplicationFactory
-at. Ha a fejlesztő által példányosított objektumok ártalmatlanítást igényelnek, azokat a IClassFixture
implementációban kell megsemmisíteni. További információért lásd: Dispose metódus implementálása.
Integrációs tesztek mintája
A mintaalkalmazás két alkalmazásból áll:
Alkalmazás | Projektmappa | Leírás |
---|---|---|
Üzenetküldő alkalmazás (a SUT) | src/RazorPagesProject |
Lehetővé teszi a felhasználó számára, hogy hozzáadjon, töröljön egyet, törölje az összeset, és elemezze az üzeneteket. |
Alkalmazás tesztelése | tests/RazorPagesProject.Tests |
A SUT integrációs tesztelésére szolgál. |
A tesztek egy IDE beépített tesztfunkcióival futtathatók, például Visual Studio. Ha Visual Studio Code vagy parancssort használ, hajtsa végre a következő parancsot egy parancssorban a tests/RazorPagesProject.Tests
könyvtárban:
dotnet test
Üzenetküldő alkalmazás (SUT) szervezete
A SUT egy Razor Pages üzenetrendszer, amely a következő jellemzőkkel rendelkezik:
- Az alkalmazás indexoldala (
Pages/Index.cshtml
ésPages/Index.cshtml.cs
) felhasználói felületi és lapmodellezési módszereket biztosít az üzenetek hozzáadásának, törlésének és elemzésének szabályozásához (átlagos szavak üzenetenként). - Az üzeneteket a
Message
osztály (Data/Message.cs
) írja le két tulajdonságokkal:Id
(kulcs) ésText
(üzenet). AText
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 egy adatelérési réteget (DAL) tartalmaz az adatbázis környezeti osztályában,
AppDbContext
(Data/AppDbContext.cs
). - Ha az adatbázis üres az alkalmazás indításakor, az üzenettároló három üzenettel lesz inicializálva.
- Az alkalmazás tartalmaz egy
/SecurePage
, amelyet csak hitelesített felhasználó érhet el.
†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 az alkalmazás nem használja az adattár mintát, és nem hatékony példa a Munkaegység (UoW) mintára, a Razor Pages támogatja ezeket a fejlesztési mintákat. További információ: Az infrastruktúra perzisztenciaréteg tervezése és Tesztvezérlő logika (a minta implementálja az adattár mintát).
Tesztalkalmazás szervezése
A tesztalkalmazás egy konzolalkalmazás a tests/RazorPagesProject.Tests
könyvtárban.
Alkalmazáskönyvtár tesztelése | Leírás |
---|---|
AuthTests |
A következő tesztelési módszereket tartalmazza:
|
BasicTests |
Az útválasztás és a tartalomtípus tesztelési módszerét tartalmazza. |
IntegrationTests |
Az Index lap integrációs tesztjeit tartalmazza egyéni WebApplicationFactory osztály használatával. |
Helpers/Utilities |
|
A tesztelési keretrendszer xUnit. Az integrációs teszteket a Microsoft.AspNetCore.TestHostsegítségével hajtják végre, amely magában foglalja a TestServer-et. Mivel a Microsoft.AspNetCore.Mvc.Testing
csomag a tesztgazda és a tesztkiszolgáló konfigurálására szolgál, a TestHost
és TestServer
csomagok nem igényelnek közvetlen csomaghivatkozásokat a tesztalkalmazás projektfájljában vagy fejlesztői konfigurációjában a tesztalkalmazásban.
Az integrációs tesztek általában kis adatkészletet igényelnek az adatbázisban a teszt végrehajtása előtt. A törlési teszt például egy adatbázisrekord törlését kéri, ezért az adatbázisnak legalább egy rekorddal kell rendelkeznie ahhoz, hogy a törlési kérelem sikeres legyen.
A mintaalkalmazás három üzenettel tölti fel az adatbázist a Utilities.cs
-ben, amelyeket a tesztek futtatásakor használhatnak.
public static void InitializeDbForTests(ApplicationDbContext db)
{
db.Messages.AddRange(GetSeedingMessages());
db.SaveChanges();
}
public static void ReinitializeDbForTests(ApplicationDbContext db)
{
db.Messages.RemoveRange(db.Messages);
InitializeDbForTests(db);
}
public static List<Message> GetSeedingMessages()
{
return new List<Message>()
{
new Message(){ Text = "TEST RECORD: You're standing on my scarf." },
new Message(){ Text = "TEST RECORD: Would you like a jelly baby?" },
new Message(){ Text = "TEST RECORD: To the rational mind, " +
"nothing is inexplicable; only unexplained." }
};
}
A SUT adatbázis-környezete a Startup.ConfigureServices
metódusában van regisztrálva. A tesztalkalmazás builder.ConfigureServices
visszahívása az alkalmazás lesz végrehajtva. Ha másik adatbázist szeretne használni a tesztekhez, az alkalmazás adatbázis-környezetét le kell cserélni a builder.ConfigureServices
-ban. A további információkért lásd a WebApplicationFactory testreszabása szakaszt.
Azokban az esetekben, amikor a SUT-ok továbbra is a Web Host-ot használják, a tesztalkalmazás builder.ConfigureServices
visszahívása még a SUT Startup.ConfigureServices
kódja előtt kerül végrehajtásra. A tesztalkalmazás builder.ConfigureTestServices
visszahívása után lesz végrehajtva.
További erőforrások
Ez a cikk feltételezi az egységtesztek alapszintű megértését. Ha nem ismeri a tesztelési fogalmakat, tekintse meg a .NET Core és a .NET Standard Egységtesztelés című cikket és annak csatolt tartalmát.
Mintakód megtekintése vagy letöltése (hogyan kell letölteni)
A mintaalkalmazás egy Razor Pages-alkalmazás, és feltételezi, hogy alapszintű ismeretekkel rendelkezik a Razor Pages-ről. Ha nem ismeri a Razor oldalak tartalmát, olvassa el a következő cikkeket.
Az SLA-kteszteléséhez olyan eszközt ajánlunk, mint például a .NET-Playwright, amely automatizálhatja a böngészőt.
Bevezetés az integrációs tesztekbe
Az integrációs tesztek az alkalmazás összetevőit szélesebb szinten értékelik ki, mint egységtesztek. Az egységtesztek izolált szoftverösszetevők, például az egyes osztálymódszerek tesztelésére szolgálnak. Az integrációs tesztek megerősítik, hogy két vagy több alkalmazásösszetevő együttműködik a várt eredmény érdekében, beleértve a kérések teljes feldolgozásához szükséges összes összetevőt is.
Ezek a szélesebb körű tesztek az alkalmazás infrastruktúrájának és teljes keretrendszerének tesztelésére szolgálnak, gyakran a következő összetevőket is beleértve:
- Adatbázis
- Fájlrendszer
- Hálózati berendezések
- Kérelem-válasz folyamat
Az egységtesztek az infrastruktúra-összetevők helyett létrehozott összetevőket használnak, más néven hamis vagy szimulált objektumokat.
Az egységtesztekkel ellentétben az integrációs tesztek:
- Használja azokat a tényleges összetevőket, amelyeket az alkalmazás éles környezetben használ.
- További kód- és adatfeldolgozást igényel.
- A futtatás hosszabb időt vesz igénybe.
Ezért az integrációs tesztek használatának korlátozása a legfontosabb infrastruktúra-forgatókönyvekre. Ha egy viselkedést egységteszt vagy integrációs teszt használatával lehet tesztelni, válassza ki az egységtesztet.
Az integrációs tesztek megvitatása során a tesztelt projektet gyakran nevezik System Under Test, vagy röviden "SUT" néven. A cikk során a "SUT" a tesztelt ASP.NET Core-alkalmazásra hivatkozik.
Ne írjon integrációs teszteket az adatbázisok és fájlrendszerek minden permutációs adat- és fájlhozzáféréshez. Függetlenül attól, hogy az alkalmazások hány helyen használják az adatbázisokat és a fájlrendszereket, az olvasási, írási, frissítési és törlési integrációs tesztek koncentrált készlete általában képes megfelelően tesztelni az adatbázis- és fájlrendszer-összetevőket. Használjon egységteszteket az ezen összetevőkkel interakcióba lépő metóduslogika rutintesztjeihez. Az egységtesztekben az infrastruktúra hamis vagy hamisított használata gyorsabb tesztvégrehajtást eredményez.
ASP.NET Core integrációs tesztek
A ASP.NET Core integrációs tesztjeihez a következők szükségesek:
- A tesztprojektek a teszteket tartalmazzák és hajtják végre. A tesztprojekt hivatkozással rendelkezik a SUT-ra.
- A tesztprojekt létrehoz egy tesztwebhely-gazdagépet a SUT számára, és egy tesztkiszolgáló-ügyféllel kezeli a kérelmeket és a válaszokat a SUT-val.
- A tesztfuttató a tesztek végrehajtására és a teszteredmények jelentésére szolgál.
Az integrációs tesztek olyan események sorozatát követik, amelyek magukban foglalják a szokásos Előkészület, Végrehajtásés Ellenőrzés tesztelési lépéseket.
- Az SUT webhoszt konfigurálva van.
- Létrejön egy tesztkiszolgáló-ügyfél, amely kéréseket küld az alkalmazásnak.
- A Arrange tesztelési lépés végrehajtása: A tesztalkalmazás előkészít egy kérést.
- A törvény tesztlépés végrehajtása: Az ügyfél elküldi a kérést, és megkapja a választ.
- A ellenőrzési tesztlépést végrehajtják: A tényleges választ a rendszer a várt válasz alapján érvényesíti, és érvényesnek nyilvánítja (), vagy sikertelennek ().
- A folyamat az összes teszt végrehajtásáig folytatódik.
- A teszteredmények jelentésre kerültek.
A teszt webgazda általában eltérő konfigurációval van beállítva, mint az alkalmazás normál webgazdája a tesztek futtatásához. Előfordulhat például, hogy a tesztekhez egy másik adatbázist vagy különböző alkalmazásbeállításokat használnak.
Az infrastruktúra-összetevőket, például a tesztweb-gazdagépet és a memóriabeli tesztkiszolgálót (TestServer) a Microsoft.AspNetCore.Mvc.Testing csomag biztosítja vagy felügyeli. A csomag használata leegyszerűsíti a tesztek létrehozását és végrehajtását.
A Microsoft.AspNetCore.Mvc.Testing
csomag a következő feladatokat kezeli:
- Másolja a függőségi fájlt (
.deps
) a SUT-ból a tesztprojektbin
könyvtárába. - A tartalomgyökerét-re a SUT projektgyökeréhez állítja, hogy a statikus fájlok és a lapok/nézetek megtalálhatók legyenek a tesztek végrehajtásakor.
- A WebApplicationFactory osztály gördülékenyebbé teszi a SUT
TestServer
használatával történő rendszerindítását.
Az egységtesztek dokumentációja ismerteti a tesztprojektek és tesztfuttatók beállításának módját, valamint a tesztek futtatásának részletes utasításait, valamint a tesztek és tesztosztályok elnevezésére vonatkozó javaslatokat.
Egységtesztek elkülönítése az integrációs tesztektől különböző projektekre. A tesztek elkülönítése:
- Segít biztosítani, hogy az infrastruktúra tesztelési összetevői véletlenül ne legyenek belefoglalva az egységtesztekbe.
- Lehetővé teszi a tesztkészlet futtatásának szabályozását.
Gyakorlatilag nincs különbség a Razor Pages-alkalmazások és az MVC-alkalmazások tesztjeinek konfigurációja között. Az egyetlen különbség a tesztek elnevezésében van. Egy Razor Pages-alkalmazásban az oldalvégpontok tesztjeit általában az oldalmodell-osztályról nevezik el (például IndexPageTests
az indexlap összetevőintegrációjának teszteléséhez). Az MVC-alkalmazásokban a tesztek általában vezérlőosztályok szerint vannak rendszerezve, és az általuk tesztelt vezérlőkről nevezik el őket (például HomeControllerTests
a Home vezérlő összetevőintegrációjának teszteléséhez).
Alkalmazás előfeltételeinek tesztelése
A tesztprojektnek a következőnek kell lennie:
- Hivatkozzon a
Microsoft.AspNetCore.Mvc.Testing
csomagra. - Adja meg a webes SDK-t a projektfájlban (
<Project Sdk="Microsoft.NET.Sdk.Web">
).
Ezek az előfeltételek láthatók a mintaalkalmazás-ban. Vizsgálja meg a tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj
fájlt. A mintaalkalmazás az xUnit tesztelési keretrendszert és a AngleSharp elemzőtárat használja, így a mintaalkalmazás a következőre is hivatkozik:
A xunit.runner.visualstudio
2.4.2-es vagy újabb verzióját használó alkalmazásokban a tesztprojektnek hivatkoznia kell a Microsoft.NET.Test.Sdk
csomagra.
Az Entity Framework Core a tesztekben is használatos. Tekintse meg a projektfájlt a GitHub.
SUT-környezet
Ha a SUT környezet nincs beállítva, a környezet alapértelmezés szerint a Fejlesztési környezet.
Alapszintű tesztek az alapértelmezett WebApplicationFactory használatával
Tegye közzé az implicit módon definiált Program
osztályt a tesztprojektben az alábbi műveletek egyikével:
Belső típusok kitétele a webalkalmazásból a tesztprojekt számára. Ez a SUT-projekt fájljában (
.csproj
) végezhető el:<ItemGroup> <InternalsVisibleTo Include="MyTestProject" /> </ItemGroup>
Tegye nyilvánossá a
Program
osztályt egy részleges osztály deklarációval:var builder = WebApplication.CreateBuilder(args); // ... Configure services, routes, etc. app.Run(); + public partial class Program { }
A mintaalkalmazás a
Program
részleges osztály megközelítést használja.
A WebApplicationFactory<TEntryPoint>-t használjuk egy TestServer létrehozásához az integrációs tesztekhez.
TEntryPoint
a SUT belépési pontosztálya, általában Program.cs
.
A tesztosztályok implementálnak egy osztály-fix technikai felületet (IClassFixture
), amely jelzi, hogy az osztály teszteket tartalmaz, és megosztott objektumpéldányokat biztosít az osztály tesztjei során.
Az alábbi tesztosztály BasicTests
a WebApplicationFactory
használatával végzi a SUT inicializálását, és biztosít egy HttpClient-t a Get_EndpointsReturnSuccessAndCorrectContentType
tesztmetódus számára. A metódus ellenőrzi, hogy a válasz állapotkódja sikeres-e (200-299), és a Content-Type
fejléc több alkalmazásoldalon text/html; charset=utf-8
.
CreateClient() létrehoz egy HttpClient
-példányt, amely automatikusan követi az átirányításokat és kezeli a cookie-kat.
public class BasicTests
: IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
public BasicTests(WebApplicationFactory<Program> factory)
{
_factory = factory;
}
[Theory]
[InlineData("/")]
[InlineData("/Index")]
[InlineData("/About")]
[InlineData("/Privacy")]
[InlineData("/Contact")]
public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.Equal("text/html; charset=utf-8",
response.Content.Headers.ContentType.ToString());
}
}
Alapértelmezés szerint a nem alapvető cookie-k nem maradnak meg a kérések között, ha engedélyezve van az általános adatvédelmi rendelet hozzájárulási szabályzata. A nem alapvető cookie-k( például a TempData-szolgáltató által használt) megőrzése érdekében jelölje meg őket alapvetőként a tesztek során. A cookie alapvetőként való megjelölésére vonatkozó utasításokért lásd: Alapvető cookie-k.
AngleSharp és Application Parts
az antiforgery-ellenőrzésekhez
Ez a cikk a AngleSharp elemzőt használja az antiforgery-ellenőrzések kezelésére oldalak betöltése és a HTML elemzése révén. A vezérlő végpontjainak és a Razor oldalak nézeteinek részletesebb teszteléséhez, anélkül, hogy törődne azzal, hogyan jelennek meg a böngészőben, fontolja meg a Application Parts
használatát. Az Alkalmazásrészek megközelítés egy vezérlőt vagy Razor lapot injektál az alkalmazásba, amellyel JSON-kéréseket lehet küldeni a szükséges értékek lekéréséhez. További információért lásd a blogot: Integrációs tesztelés ASP.NET Core erőforrások Antiforgery-vel történő védelme az Alkalmazás Részei segítségével és a kapcsolódó GitHub-adattár szerzője: Martin Costello.
A WebApplicationFactory testreszabása
A webtárhely konfigurációja a tesztosztályoktól függetlenül hozható létre, úgy, hogy a WebApplicationFactory<TEntryPoint>-ból örökölt egy vagy több egyéni gyárat készítünk.
Örököljön
WebApplicationFactory
és bírálja felül ConfigureWebHost. A IWebHostBuilder lehetővé teszi a szolgáltatásgyűjtemény konfigurálásátIWebHostBuilder.ConfigureServices
public class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<TProgram> where TProgram : class { protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureServices(services => { var dbContextDescriptor = services.SingleOrDefault( d => d.ServiceType == typeof(DbContextOptions<ApplicationDbContext>)); services.Remove(dbContextDescriptor); var dbConnectionDescriptor = services.SingleOrDefault( d => d.ServiceType == typeof(DbConnection)); services.Remove(dbConnectionDescriptor); // Create open SqliteConnection so EF won't automatically close it. services.AddSingleton<DbConnection>(container => { var connection = new SqliteConnection("DataSource=:memory:"); connection.Open(); return connection; }); services.AddDbContext<ApplicationDbContext>((container, options) => { var connection = container.GetRequiredService<DbConnection>(); options.UseSqlite(connection); }); }); builder.UseEnvironment("Development"); } }
Az adatbázis-vetést a mintaalkalmazásban a
InitializeDbForTests
metódus végzi. A módszert az integrációs tesztek mintájában ismertetjük: Az alkalmazás szervezetének tesztelése szakasz.A SUT adatbázis-környezetét regisztrálták
Program.cs
-ben. A tesztalkalmazásbuilder.ConfigureServices
visszahívása az alkalmazás lesz végrehajtva. Ha más adatbázist szeretne használni a tesztekhez, mint az alkalmazás adatbázisa, az alkalmazás adatbázis-környezetét abuilder.ConfigureServices
adatbázis-környezetre kell cserélni.A mintaalkalmazás megkeresi az adatbázis-környezet szolgáltatásleíróját, és a leíró használatával eltávolítja a szolgáltatásregisztrációt. A gyár ezután hozzáad egy új
ApplicationDbContext
, amely memórián belüli adatbázist használ a tesztekhez.Ha másik adatbázishoz szeretne csatlakozni, módosítsa a
DbConnection
. SQL Server tesztadatbázis használata:
- A projektfájlban hivatkozzon a
Microsoft.EntityFrameworkCore.SqlServer
NuGet-csomagra. - Hívja
UseInMemoryDatabase
.
Az egyéni
CustomWebApplicationFactory
-t használja a tesztosztályokban. Az alábbi példa a gyárat használja aIndexPageTests
osztályban:public class IndexPageTests : IClassFixture<CustomWebApplicationFactory<Program>> { private readonly HttpClient _client; private readonly CustomWebApplicationFactory<Program> _factory; public IndexPageTests( CustomWebApplicationFactory<Program> factory) { _factory = factory; _client = factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false }); }
A mintaalkalmazás ügyfele úgy van konfigurálva, hogy megakadályozza a
HttpClient
átirányítások követését. Amint azt a Mock authentication szakasz későbbi szakasza ismerteti, ez lehetővé teszi a tesztek számára az alkalmazás első válaszának eredményének ellenőrzését. Az első válasz ezek közül sok tesztben egy átirányítás egy "Location
" fejléccel.Egy tipikus teszt a
HttpClient
és segédmetenek használatával dolgozza fel a kérést és a választ:[Fact] public async Task Post_DeleteAllMessagesHandler_ReturnsRedirectToRoot() { // Arrange var defaultPage = await _client.GetAsync("/"); var content = await HtmlHelpers.GetDocumentAsync(defaultPage); //Act var response = await _client.SendAsync( (IHtmlFormElement)content.QuerySelector("form[id='messages']"), (IHtmlButtonElement)content.QuerySelector("button[id='deleteAllBtn']")); // Assert Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode); Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); Assert.Equal("/", response.Headers.Location.OriginalString); }
A SUT-nak küldött POST-kéréseknek meg kell felelniük az alkalmazás adatvédelmi rendszerének automatikusan végrehajtott hamisítás elleni ellenőrzési folyamatának. A teszt POST-kérésének rendezéséhez a tesztalkalmazásnak a következőnek kell lennie:
- Kérjen egy kérelmet az oldalhoz.
- Elemezze az azonosítóhamisítás elleni cookie-t, és kérje le az érvényesítési jogkivonatot a választól.
- Végezze el a POST kérést az antiforgery cookie és az érvényesítési jogkivonattal.
A mintaalkalmazás SendAsync
segédmetódusai (Helpers/HttpClientExtensions.cs
) és a GetDocumentAsync
segédmetódus (Helpers/HtmlHelpers.cs
) az AngleSharp elemzővel kezelik az antiforgery-ellenőrzést az alábbi módszerekkel:
-
GetDocumentAsync
: Megkapja a HttpResponseMessage, és visszaad egyIHtmlDocument
.GetDocumentAsync
egy olyan gyárat használ, amely az eredeti alapján készít elő egyHttpResponseMessage
-t. További információért tekintse meg az AngleSharp dokumentációt. -
SendAsync
kiterjesztési metódusai aHttpClient
összállítanak egy HttpRequestMessage-t, és meghívják a SendAsync(HttpRequestMessage)-t, hogy kérelmeket küldjenek a SUT-nak. ASendAsync
túlterhelései elfogadják a HTML-űrlapot (IHtmlFormElement
) és a következőket:- Az űrlap Küldés gombja (
IHtmlElement
) - Űrlapértékek gyűjtése (
IEnumerable<KeyValuePair<string, string>>
) - Küldés gomb (
IHtmlElement
) és űrlapértékek (IEnumerable<KeyValuePair<string, string>>
)
- Az űrlap Küldés gombja (
AngleSharp egy harmadik féltől származó elemzési kódtár, amelyet bemutató célokra használnak, ebben a cikkben és a mintaalkalmazásban. Az AngleSharp nem támogatott vagy szükséges ASP.NET Core-alkalmazások integrációs teszteléséhez. Más elemzők is használhatók, például a Html Agility Pack (HAP). Egy másik módszer, ha olyan kódot írunk, amely közvetlenül kezeli az antiforgery rendszer kérés-ellenőrzési tokenjét és az antiforgery cookie-t. További információkért tekintse meg az antiforgery-ellenőrzésekről szóló cikket itt: AngleSharp vs Application Parts
,.
A EF-Core memóriabeli adatbázis-szolgáltató korlátozott és alapszintű teszteléshez használható, de a SQLite-szolgáltató a memóriabeli teszteléshez ajánlott.
Lásd: Indítás kiterjesztése indítási szűrőkkel, amely bemutatja, hogyan konfigurálhat köztes szoftvereket IStartupFilterhasználatával, ami akkor hasznos, ha egy teszthez egyéni szolgáltatásra vagy köztes szoftverre van szükség.
Az ügyfél testreszabása a WithWebHostBuilder használatával
Ha egy tesztmetóduson belül további konfigurációra van szükség, WithWebHostBuilder létrehoz egy új WebApplicationFactory
egy olyan IWebHostBuilder-vel, amelyet a konfiguráció további testreszab.
A mintakód meghívja WithWebHostBuilder
, hogy a konfigurált szolgáltatásokat tesztcsomókkal cserélje le. További információért és a példák használatáért lásd a Modellszolgáltatások injektálása rész ebben a cikkben.
A mintaalkalmazás Post_DeleteMessageHandler_ReturnsRedirectToRoot
tesztmetódusa a WithWebHostBuilder
használatát mutatja be. Ez a teszt egy rekord törlését hajtja végre az adatbázisban egy űrlapbeküldés SUT-ban való aktiválásával.
Mivel a IndexPageTests
osztály egy másik tesztje olyan műveletet hajt végre, amely törli az adatbázis összes rekordját, és a Post_DeleteMessageHandler_ReturnsRedirectToRoot
metódus előtt futhat, az adatbázist ebben a tesztmetódusban újból el kell végezni, hogy a rendszer egy rekordot adjon meg a SUT számára a törléshez. A SUT-ban a messages
űrlap első törlési gombjának kiválasztását a rendszer szimulálja a SUT-nak küldött kérésben:
[Fact]
public async Task Post_DeleteMessageHandler_ReturnsRedirectToRoot()
{
// Arrange
using (var scope = _factory.Services.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<ApplicationDbContext>();
Utilities.ReinitializeDbForTests(db);
}
var defaultPage = await _client.GetAsync("/");
var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
//Act
var response = await _client.SendAsync(
(IHtmlFormElement)content.QuerySelector("form[id='messages']"),
(IHtmlButtonElement)content.QuerySelector("form[id='messages']")
.QuerySelector("div[class='panel-body']")
.QuerySelector("button"));
// Assert
Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode);
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.Equal("/", response.Headers.Location.OriginalString);
}
Ügyfélbeállítások
A WebApplicationFactoryClientOptions példányok létrehozásakor az alapértelmezett beállításokat és az elérhető beállításokat a HttpClient
lapon tekinti meg.
Hozza létre a WebApplicationFactoryClientOptions
osztályt, és adja át a CreateClient() metódusnak:
public class IndexPageTests :
IClassFixture<CustomWebApplicationFactory<Program>>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory<Program>
_factory;
public IndexPageTests(
CustomWebApplicationFactory<Program> factory)
{
_factory = factory;
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
}
MEGJEGYZÉS: A HTTPS-átirányítási figyelmeztetések elkerülése érdekében, amikor a HTTPS-átirányító köztes réteget használja, állítsa be a BaseAddress = new Uri("https://localhost")
Makettszolgáltatások injektálása
A szolgáltatások felülírhatók egy tesztben a ConfigureTestServices hívásával a gazdagép építőn. A felülbírált szolgáltatások konkrét teszt hatóköréhez a WithWebHostBuilder metódus használható egy hostkészítő lekérésére. Ez a következő tesztekben látható:
- Get_QuoteService_ProvidesQuoteInPage
- Szerezd_MegGithubProfilOldalAmiElérhetEgyGithubFelhasználót
- Biztonságos_ElőoldalVisszaadvaHitelesítettFelhasználónak
A minta SUT tartalmaz egy hatókörű szolgáltatást, amely egy idézetet ad vissza. Az indexlap kérésekor az idézet az Index lap rejtett mezőjébe van beágyazva.
Services/IQuoteService.cs
:
public interface IQuoteService
{
Task<string> GenerateQuote();
}
Services/QuoteService.cs
:
// Quote ©1975 BBC: The Doctor (Tom Baker); Dr. Who: Planet of Evil
// https://www.bbc.co.uk/programmes/p00pyrx6
public class QuoteService : IQuoteService
{
public Task<string> GenerateQuote()
{
return Task.FromResult<string>(
"Come on, Sarah. We've an appointment in London, " +
"and we're already 30,000 years late.");
}
}
Program.cs
:
services.AddScoped<IQuoteService, QuoteService>();
Pages/Index.cshtml.cs
:
public class IndexModel : PageModel
{
private readonly ApplicationDbContext _db;
private readonly IQuoteService _quoteService;
public IndexModel(ApplicationDbContext db, IQuoteService quoteService)
{
_db = db;
_quoteService = quoteService;
}
[BindProperty]
public Message Message { get; set; }
public IList<Message> Messages { get; private set; }
[TempData]
public string MessageAnalysisResult { get; set; }
public string Quote { get; private set; }
public async Task OnGetAsync()
{
Messages = await _db.GetMessagesAsync();
Quote = await _quoteService.GenerateQuote();
}
Pages/Index.cs
:
<input id="quote" type="hidden" value="@Model.Quote">
A SUT-alkalmazás futtatásakor a következő korrektúra jön létre:
<input id="quote" type="hidden" value="Come on, Sarah. We've an appointment in
London, and we're already 30,000 years late.">
A szolgáltatás és az injektálás teszteléséhez integrációs tesztben a teszt egy ál-szolgáltatást injektál a SUT-ba. A modellszolgáltatás lecseréli az alkalmazás QuoteService
-t a tesztalkalmazás által biztosított, TestQuoteService
nevű szolgáltatásra.
IntegrationTests.IndexPageTests.cs
:
// Quote ©1975 BBC: The Doctor (Tom Baker); Pyramids of Mars
// https://www.bbc.co.uk/programmes/p00pys55
public class TestQuoteService : IQuoteService
{
public Task<string> GenerateQuote()
{
return Task.FromResult(
"Something's interfering with time, Mr. Scarman, " +
"and time is my business.");
}
}
ConfigureTestServices
van meghívva, és a hatókörön belüli szolgáltatás regisztrálva van:
[Fact]
public async Task Get_QuoteService_ProvidesQuoteInPage()
{
// Arrange
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddScoped<IQuoteService, TestQuoteService>();
});
})
.CreateClient();
//Act
var defaultPage = await client.GetAsync("/");
var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
var quoteElement = content.QuerySelector("#quote");
// Assert
Assert.Equal("Something's interfering with time, Mr. Scarman, " +
"and time is my business.", quoteElement.Attributes["value"].Value);
}
A teszt végrehajtása során létrehozott jelölések a TestQuoteService
által megadott idézőjelszöveget tükrözik, ezért az állítás sikeres.
<input id="quote" type="hidden" value="Something's interfering with time,
Mr. Scarman, and time is my business.">
Szimulált hitelesítés
Az AuthTests
osztály tesztjei ellenőrzik, hogy egy biztonságos végpont:
- Átirányít egy nem hitelesített felhasználót az alkalmazás bejelentkezési oldalára.
- Egy hitelesített felhasználó tartalmát adja vissza.
A SUT-ban a /SecurePage
lap a AuthorizePage konvenciót használja, hogy egy AuthorizeFilter-t alkalmazzon a lapra. További információkért lásd a Razor oldalak engedélyezési konvencióit.
services.AddRazorPages(options =>
{
options.Conventions.AuthorizePage("/SecurePage");
});
A Get_SecurePageRedirectsAnUnauthenticatedUser
tesztben egy WebApplicationFactoryClientOptions úgy van beállítva, hogy letiltsa az átirányításokat azáltal, hogy AllowAutoRedirect-t false
-ra állítja.
[Fact]
public async Task Get_SecurePageRedirectsAnUnauthenticatedUser()
{
// Arrange
var client = _factory.CreateClient(
new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
// Act
var response = await client.GetAsync("/SecurePage");
// Assert
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.StartsWith("http://localhost/Identity/Account/Login",
response.Headers.Location.OriginalString);
}
Az ügyfél átirányításának megakadályozásával a következő ellenőrzéseket hajthatja végre:
- A SUT által visszaadott állapotkód a várt HttpStatusCode.Redirect eredményen ellenőrizhető, nem pedig a bejelentkezési lapra való átirányítás utáni végleges állapotkóddal, amely HttpStatusCode.OK.
- A válaszfejlécek közül a
Location
fejléc értékét ellenőrzik annak megállapítására, hogyhttp://localhost/Identity/Account/Login
-el kezdődik-e, nem pedig a végleges bejelentkezési oldal válaszát, ahol aLocation
fejléc nem lenne jelen.
A tesztalkalmazás szimulálhat egy AuthenticationHandler<TOptions>ConfigureTestServices-ben a hitelesítés és az engedélyezés szempontjainak teszteléséhez. Egy minimális forgatókönyv AuthenticateResult.Success-t ad vissza.
public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var claims = new[] { new Claim(ClaimTypes.Name, "Test user") };
var identity = new ClaimsIdentity(claims, "Test");
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, "TestScheme");
var result = AuthenticateResult.Success(ticket);
return Task.FromResult(result);
}
}
A TestAuthHandler
egy felhasználó hitelesítésére hívják meg, amikor a hitelesítési séma TestScheme
van beállítva, ahol a AddAuthentication
regisztrálva van a ConfigureTestServices
-ra. Fontos, hogy a TestScheme
séma megfeleljen az alkalmazás által várt sémának. Ellenkező esetben a hitelesítés nem működik.
[Fact]
public async Task Get_SecurePageIsReturnedForAnAuthenticatedUser()
{
// Arrange
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddAuthentication(defaultScheme: "TestScheme")
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
"TestScheme", options => { });
});
})
.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false,
});
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(scheme: "TestScheme");
//Act
var response = await client.GetAsync("/SecurePage");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
A WebApplicationFactoryClientOptions
kapcsolatos további információkért lásd a Ügyfélbeállítások című szakaszt.
A köztes szoftver hitelesítésének alapszintű tesztjei
A Köztes szoftver hitelesítésének alapszintű teszteléséhez tekintse meg ezt a GitHub-adattárat. A tesztforgatókönyvre jellemző tesztkiszolgálót tartalmaz.
A környezet beállítása
Állítsa be a környezetet az egyéni alkalmazás-előállítóban:
public class CustomWebApplicationFactory<TProgram>
: WebApplicationFactory<TProgram> where TProgram : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var dbContextDescriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbContextOptions<ApplicationDbContext>));
services.Remove(dbContextDescriptor);
var dbConnectionDescriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbConnection));
services.Remove(dbConnectionDescriptor);
// Create open SqliteConnection so EF won't automatically close it.
services.AddSingleton<DbConnection>(container =>
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
return connection;
});
services.AddDbContext<ApplicationDbContext>((container, options) =>
{
var connection = container.GetRequiredService<DbConnection>();
options.UseSqlite(connection);
});
});
builder.UseEnvironment("Development");
}
}
Hogyan következtet a tesztinfrastruktúra az alkalmazás tartalomgyökerének elérési útjára?
A WebApplicationFactory
konstruktor meghatározza az alkalmazás tartalom gyökér elérési útját úgy, hogy a WebApplicationFactoryContentRootAttribute szerelvény TEntryPoint
kulccsal a System.Reflection.Assembly.FullName
-t keres az integrációs teszteket tartalmazó szerelvényen. Ha nem található a megfelelő kulccsal rendelkező attribútum, WebApplicationFactory
visszatér a megoldásfájl (.sln) kereséséhez, és hozzáadja a TEntryPoint
szerelvény nevét a megoldáskönyvtárhoz. Az alkalmazás gyökérkönyvtára (a tartalom gyökérútvonala) a nézetek és tartalomfájlok felderítésére szolgál.
Árnyékmásolás letiltása
Az árnyékmásolás miatt a tesztek a kimeneti könyvtártól eltérő könyvtárban futnak. Ha a tesztek a fájlok Assembly.Location
-hoz viszonyított betöltésére támaszkodnak, és problémákat tapasztalunk, előfordulhat, hogy le kell tiltani az árnyékmásolást.
Ha le szeretné tiltani az árnyékmásolást az xUnit használatakor, hozzon létre egy xunit.runner.json
fájlt a tesztprojekt könyvtárában a megfelelő konfigurációs beállítással:
{
"shadowCopy": false
}
Objektumok ártalmatlanítása
A IClassFixture
implementáció tesztjeinek végrehajtása után TestServer és HttpClient megsemmisülnek, amikor az xUnit megsemmisíti a WebApplicationFactory
-at. Ha a fejlesztő által példányosított objektumok ártalmatlanítást igényelnek, azokat a IClassFixture
implementációban kell megsemmisíteni. További információért lásd: Dispose metódus implementálása.
Integrációs tesztek mintája
A mintaalkalmazás két alkalmazásból áll:
Alkalmazás | Projektmappa | Leírás |
---|---|---|
Üzenetküldő alkalmazás (a SUT) | src/RazorPagesProject |
Lehetővé teszi a felhasználó számára, hogy hozzáadjon, töröljön egyet, törölje az összeset, és elemezze az üzeneteket. |
Alkalmazás tesztelése | tests/RazorPagesProject.Tests |
A SUT integrációs tesztelésére szolgál. |
A tesztek egy IDE beépített tesztfunkcióival futtathatók, például Visual Studio. Ha Visual Studio Code vagy parancssort használ, hajtsa végre a következő parancsot egy parancssorban a tests/RazorPagesProject.Tests
könyvtárban:
dotnet test
Üzenetküldő alkalmazás (SUT) szervezete
A SUT egy Razor Pages üzenetrendszer, amely a következő jellemzőkkel rendelkezik:
- Az alkalmazás indexoldala (
Pages/Index.cshtml
ésPages/Index.cshtml.cs
) felhasználói felületi és lapmodellezési módszereket biztosít az üzenetek hozzáadásának, törlésének és elemzésének szabályozásához (átlagos szavak üzenetenként). - Az üzeneteket a
Message
osztály (Data/Message.cs
) írja le két tulajdonságokkal:Id
(kulcs) ésText
(üzenet). AText
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 egy adatelérési réteget (DAL) tartalmaz az adatbázis környezeti osztályában,
AppDbContext
(Data/AppDbContext.cs
). - Ha az adatbázis üres az alkalmazás indításakor, az üzenettároló három üzenettel lesz inicializálva.
- Az alkalmazás tartalmaz egy
/SecurePage
, amelyet csak hitelesített felhasználó érhet el.
†Az EF-cikk, Az InMemorytesztelése című cikk 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 az alkalmazás nem használja az adattár mintát, és nem hatékony példa a Munkaegység (UoW) mintára, a Razor Pages támogatja ezeket a fejlesztési mintákat. További információ: Az infrastruktúra perzisztenciaréteg tervezése és Tesztvezérlő logika (a minta implementálja az adattár mintát).
Tesztalkalmazás szervezése
A tesztalkalmazás egy konzolalkalmazás a tests/RazorPagesProject.Tests
könyvtárban.
Alkalmazáskönyvtár tesztelése | Leírás |
---|---|
AuthTests |
A következő tesztelési módszereket tartalmazza:
|
BasicTests |
Az útválasztás és a tartalomtípus tesztelési módszerét tartalmazza. |
IntegrationTests |
Az Index lap integrációs tesztjeit tartalmazza egyéni WebApplicationFactory osztály használatával. |
Helpers/Utilities |
|
A tesztelési keretrendszer xUnit. Az integrációs teszteket a Microsoft.AspNetCore.TestHostsegítségével hajtják végre, amely magában foglalja a TestServer-et. Mivel a Microsoft.AspNetCore.Mvc.Testing
csomag a tesztgazda és a tesztkiszolgáló konfigurálására szolgál, a TestHost
és TestServer
csomagok nem igényelnek közvetlen csomaghivatkozásokat a tesztalkalmazás projektfájljában vagy fejlesztői konfigurációjában a tesztalkalmazásban.
Az integrációs tesztek általában kis adatkészletet igényelnek az adatbázisban a teszt végrehajtása előtt. A törlési teszt például egy adatbázisrekord törlését kéri, ezért az adatbázisnak legalább egy rekorddal kell rendelkeznie ahhoz, hogy a törlési kérelem sikeres legyen.
A mintaalkalmazás három üzenettel tölti fel az adatbázist a Utilities.cs
-ben, amelyeket a tesztek futtatásakor használhatnak.
public static void InitializeDbForTests(ApplicationDbContext db)
{
db.Messages.AddRange(GetSeedingMessages());
db.SaveChanges();
}
public static void ReinitializeDbForTests(ApplicationDbContext db)
{
db.Messages.RemoveRange(db.Messages);
InitializeDbForTests(db);
}
public static List<Message> GetSeedingMessages()
{
return new List<Message>()
{
new Message(){ Text = "TEST RECORD: You're standing on my scarf." },
new Message(){ Text = "TEST RECORD: Would you like a jelly baby?" },
new Message(){ Text = "TEST RECORD: To the rational mind, " +
"nothing is inexplicable; only unexplained." }
};
}
A SUT adatbázis-környezetét regisztrálták Program.cs
-ben. A tesztalkalmazás builder.ConfigureServices
visszahívása az alkalmazás lesz végrehajtva. Ha másik adatbázist szeretne használni a tesztekhez, az alkalmazás adatbázis-környezetét le kell cserélni a builder.ConfigureServices
-ban. A további információkért lásd a WebApplicationFactory testreszabása szakaszt.
További erőforrások
- egységtesztek
- Razor Lapok egységtesztjei ASP.NET Core
- ASP.NET Core köztes szoftver
- Tesztvezérlő logika az ASP.NET Core-ban
- Köztes szoftver hitelesítésének alapszintű tesztjei
Ez a cikk feltételezi az egységtesztek alapszintű megértését. Ha nem ismeri a tesztelési fogalmakat, tekintse meg a .NET Core és a .NET Standard Egységtesztelés című cikket és annak csatolt tartalmát.
Mintakód megtekintése vagy letöltése (hogyan kell letölteni)
A mintaalkalmazás egy Razor Pages-alkalmazás, és feltételezi, hogy alapszintű ismeretekkel rendelkezik a Razor Pages-ről. Ha nem ismeri a Razor oldalak tartalmát, olvassa el a következő cikkeket.
Az SLA-kteszteléséhez olyan eszközt ajánlunk, mint például a .NET-Playwright, amely automatizálhatja a böngészőt.
Bevezetés az integrációs tesztekbe
Az integrációs tesztek az alkalmazás összetevőit szélesebb szinten értékelik ki, mint egységtesztek. Az egységtesztek izolált szoftverösszetevők, például az egyes osztálymódszerek tesztelésére szolgálnak. Az integrációs tesztek megerősítik, hogy két vagy több alkalmazásösszetevő együttműködik a várt eredmény érdekében, beleértve a kérések teljes feldolgozásához szükséges összes összetevőt is.
Ezek a szélesebb körű tesztek az alkalmazás infrastruktúrájának és teljes keretrendszerének tesztelésére szolgálnak, gyakran a következő összetevőket is beleértve:
- Adatbázis
- Fájlrendszer
- Hálózati berendezések
- Kérelem-válasz folyamat
Az egységtesztek az infrastruktúra-összetevők helyett létrehozott összetevőket használnak, más néven hamis vagy szimulált objektumokat.
Az egységtesztekkel ellentétben az integrációs tesztek:
- Használja azokat a tényleges összetevőket, amelyeket az alkalmazás éles környezetben használ.
- További kód- és adatfeldolgozást igényel.
- A futtatás hosszabb időt vesz igénybe.
Ezért az integrációs tesztek használatának korlátozása a legfontosabb infrastruktúra-forgatókönyvekre. Ha egy viselkedést egységteszt vagy integrációs teszt használatával lehet tesztelni, válassza ki az egységtesztet.
Az integrációs tesztek megvitatása során a tesztelt projektet gyakran nevezik System Under Test, vagy röviden "SUT" néven. A cikk során a "SUT" a tesztelt ASP.NET Core-alkalmazásra hivatkozik.
Ne írjon integrációs teszteket az adatbázisok és fájlrendszerek minden permutációs adat- és fájlhozzáféréshez. Függetlenül attól, hogy az alkalmazások hány helyen használják az adatbázisokat és a fájlrendszereket, az olvasási, írási, frissítési és törlési integrációs tesztek koncentrált készlete általában képes megfelelően tesztelni az adatbázis- és fájlrendszer-összetevőket. Használjon egységteszteket az ezen összetevőkkel interakcióba lépő metóduslogika rutintesztjeihez. Az egységtesztekben az infrastruktúra hamis vagy hamisított használata gyorsabb tesztvégrehajtást eredményez.
ASP.NET Core integrációs tesztek
A ASP.NET Core integrációs tesztjeihez a következők szükségesek:
- A tesztprojektek a teszteket tartalmazzák és hajtják végre. A tesztprojekt hivatkozással rendelkezik a SUT-ra.
- A tesztprojekt létrehoz egy tesztwebhely-gazdagépet a SUT számára, és egy tesztkiszolgáló-ügyféllel kezeli a kérelmeket és a válaszokat a SUT-val.
- A tesztfuttató a tesztek végrehajtására és a teszteredmények jelentésére szolgál.
Az integrációs tesztek olyan események sorozatát követik, amelyek magukban foglalják a szokásos Előkészület, Végrehajtásés Ellenőrzés tesztelési lépéseket.
- Az SUT webhoszt konfigurálva van.
- Létrejön egy tesztkiszolgáló-ügyfél, amely kéréseket küld az alkalmazásnak.
- A Arrange tesztelési lépés végrehajtása: A tesztalkalmazás előkészít egy kérést.
- A törvény tesztlépés végrehajtása: Az ügyfél elküldi a kérést, és megkapja a választ.
- A ellenőrzési tesztlépést végrehajtják: A tényleges választ a rendszer a várt válasz alapján érvényesíti, és érvényesnek nyilvánítja (), vagy sikertelennek ().
- A folyamat az összes teszt végrehajtásáig folytatódik.
- A teszteredmények jelentésre kerültek.
A teszt webgazda általában eltérő konfigurációval van beállítva, mint az alkalmazás normál webgazdája a tesztek futtatásához. Előfordulhat például, hogy a tesztekhez egy másik adatbázist vagy különböző alkalmazásbeállításokat használnak.
Az infrastruktúra-összetevőket, például a tesztweb-gazdagépet és a memóriabeli tesztkiszolgálót (TestServer) a Microsoft.AspNetCore.Mvc.Testing csomag biztosítja vagy felügyeli. A csomag használata leegyszerűsíti a tesztek létrehozását és végrehajtását.
A Microsoft.AspNetCore.Mvc.Testing
csomag a következő feladatokat kezeli:
- Másolja a függőségi fájlt (
.deps
) a SUT-ból a tesztprojektbin
könyvtárába. - A tartalomgyökerét-re a SUT projektgyökeréhez állítja, hogy a statikus fájlok és a lapok/nézetek megtalálhatók legyenek a tesztek végrehajtásakor.
- A WebApplicationFactory osztály gördülékenyebbé teszi a SUT
TestServer
használatával történő rendszerindítását.
Az egységtesztek dokumentációja ismerteti a tesztprojektek és tesztfuttatók beállításának módját, valamint a tesztek futtatásának részletes utasításait, valamint a tesztek és tesztosztályok elnevezésére vonatkozó javaslatokat.
Egységtesztek elkülönítése az integrációs tesztektől különböző projektekre. A tesztek elkülönítése:
- Segít biztosítani, hogy az infrastruktúra tesztelési összetevői véletlenül ne legyenek belefoglalva az egységtesztekbe.
- Lehetővé teszi a tesztkészlet futtatásának szabályozását.
Gyakorlatilag nincs különbség a Razor Pages-alkalmazások és az MVC-alkalmazások tesztjeinek konfigurációja között. Az egyetlen különbség a tesztek elnevezésében van. Egy Razor Pages-alkalmazásban az oldalvégpontok tesztjeit általában az oldalmodell-osztályról nevezik el (például IndexPageTests
az indexlap összetevőintegrációjának teszteléséhez). Az MVC-alkalmazásokban a tesztek általában vezérlőosztályok szerint vannak rendszerezve, és az általuk tesztelt vezérlőkről nevezik el őket (például HomeControllerTests
a Home vezérlő összetevőintegrációjának teszteléséhez).
Alkalmazás előfeltételeinek tesztelése
A tesztprojektnek a következőnek kell lennie:
- Hivatkozzon a
Microsoft.AspNetCore.Mvc.Testing
csomagra. - Adja meg a webes SDK-t a projektfájlban (
<Project Sdk="Microsoft.NET.Sdk.Web">
).
Ezek az előfeltételek láthatók a mintaalkalmazás-ban. Vizsgálja meg a tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj
fájlt. A mintaalkalmazás az xUnit tesztelési keretrendszert és a AngleSharp elemzőtárat használja, így a mintaalkalmazás a következőre is hivatkozik:
A xunit.runner.visualstudio
2.4.2-es vagy újabb verzióját használó alkalmazásokban a tesztprojektnek hivatkoznia kell a Microsoft.NET.Test.Sdk
csomagra.
Az Entity Framework Core a tesztekben is használatos. Tekintse meg a projektfájlt a GitHub.
SUT-környezet
Ha a SUT környezet nincs beállítva, a környezet alapértelmezés szerint a Fejlesztési környezet.
Alapszintű tesztek az alapértelmezett WebApplicationFactory használatával
Tegye közzé az implicit módon definiált Program
osztályt a tesztprojektben az alábbi műveletek egyikével:
Belső típusok kitétele a webalkalmazásból a tesztprojekt számára. Ez a SUT-projekt fájljában (
.csproj
) végezhető el:<ItemGroup> <InternalsVisibleTo Include="MyTestProject" /> </ItemGroup>
Tegye nyilvánossá a
Program
osztályt egy részleges osztály deklarációval:var builder = WebApplication.CreateBuilder(args); // ... Configure services, routes, etc. app.Run(); + public partial class Program { }
A mintaalkalmazás a
Program
részleges osztály megközelítést használja.
A WebApplicationFactory<TEntryPoint>-t használjuk egy TestServer létrehozásához az integrációs tesztekhez.
TEntryPoint
a SUT belépési pontosztálya, általában Program.cs
.
A tesztosztályok implementálnak egy osztály-fix technikai felületet (IClassFixture
), amely jelzi, hogy az osztály teszteket tartalmaz, és megosztott objektumpéldányokat biztosít az osztály tesztjei során.
Az alábbi tesztosztály BasicTests
a WebApplicationFactory
használatával végzi a SUT inicializálását, és biztosít egy HttpClient-t a Get_EndpointsReturnSuccessAndCorrectContentType
tesztmetódus számára. A metódus ellenőrzi, hogy a válasz állapotkódja sikeres-e (200-299), és a Content-Type
fejléc több alkalmazásoldalon text/html; charset=utf-8
.
CreateClient() létrehoz egy HttpClient
-példányt, amely automatikusan követi az átirányításokat és kezeli a cookie-kat.
public class BasicTests
: IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
public BasicTests(WebApplicationFactory<Program> factory)
{
_factory = factory;
}
[Theory]
[InlineData("/")]
[InlineData("/Index")]
[InlineData("/About")]
[InlineData("/Privacy")]
[InlineData("/Contact")]
public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.Equal("text/html; charset=utf-8",
response.Content.Headers.ContentType.ToString());
}
}
Alapértelmezés szerint a nem alapvető cookie-k nem maradnak meg a kérések között, ha engedélyezve van az általános adatvédelmi rendelet hozzájárulási szabályzata. A nem alapvető cookie-k( például a TempData-szolgáltató által használt) megőrzése érdekében jelölje meg őket alapvetőként a tesztek során. A cookie alapvetőként való megjelölésére vonatkozó utasításokért lásd: Alapvető cookie-k.
AngleSharp és Application Parts
az antiforgery-ellenőrzésekhez
Ez a cikk a AngleSharp elemzőt használja az antiforgery-ellenőrzések kezelésére oldalak betöltése és a HTML elemzése révén. A vezérlő végpontjainak és a Razor oldalak nézeteinek részletesebb teszteléséhez, anélkül, hogy törődne azzal, hogyan jelennek meg a böngészőben, fontolja meg a Application Parts
használatát. Az Alkalmazásrészek megközelítés egy vezérlőt vagy Razor lapot injektál az alkalmazásba, amellyel JSON-kéréseket lehet küldeni a szükséges értékek lekéréséhez. További információért lásd a blogot: Integrációs tesztelés ASP.NET Core erőforrások Antiforgery-vel történő védelme az Alkalmazás Részei segítségével és a kapcsolódó GitHub-adattár szerzője: Martin Costello.
A WebApplicationFactory testreszabása
A webtárhely konfigurációja a tesztosztályoktól függetlenül hozható létre, úgy, hogy a WebApplicationFactory<TEntryPoint>-ból örökölt egy vagy több egyéni gyárat készítünk.
Örököljön
WebApplicationFactory
és bírálja felül ConfigureWebHost. A IWebHostBuilder lehetővé teszi a szolgáltatásgyűjtemény konfigurálásátIWebHostBuilder.ConfigureServices
public class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<TProgram> where TProgram : class { protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureServices(services => { var dbContextDescriptor = services.SingleOrDefault( d => d.ServiceType == typeof(DbContextOptions<ApplicationDbContext>)); services.Remove(dbContextDescriptor); var dbConnectionDescriptor = services.SingleOrDefault( d => d.ServiceType == typeof(DbConnection)); services.Remove(dbConnectionDescriptor); // Create open SqliteConnection so EF won't automatically close it. services.AddSingleton<DbConnection>(container => { var connection = new SqliteConnection("DataSource=:memory:"); connection.Open(); return connection; }); services.AddDbContext<ApplicationDbContext>((container, options) => { var connection = container.GetRequiredService<DbConnection>(); options.UseSqlite(connection); }); }); builder.UseEnvironment("Development"); } }
Az adatbázis-vetést a mintaalkalmazásban a
InitializeDbForTests
metódus végzi. A módszert az integrációs tesztek mintájában ismertetjük: Az alkalmazás szervezetének tesztelése szakasz.A SUT adatbázis-környezetét regisztrálták
Program.cs
-ben. A tesztalkalmazásbuilder.ConfigureServices
visszahívása az alkalmazás lesz végrehajtva. Ha más adatbázist szeretne használni a tesztekhez, mint az alkalmazás adatbázisa, az alkalmazás adatbázis-környezetét abuilder.ConfigureServices
adatbázis-környezetre kell cserélni.A mintaalkalmazás megkeresi az adatbázis-környezet szolgáltatásleíróját, és a leíró használatával eltávolítja a szolgáltatásregisztrációt. A gyár ezután hozzáad egy új
ApplicationDbContext
, amely memórián belüli adatbázist használ a tesztekhez.Ha másik adatbázishoz szeretne csatlakozni, módosítsa a
DbConnection
. SQL Server tesztadatbázis használata:- A projektfájlban hivatkozzon a
Microsoft.EntityFrameworkCore.SqlServer
NuGet-csomagra. - Hívja
UseInMemoryDatabase
.
- A projektfájlban hivatkozzon a
Az egyéni
CustomWebApplicationFactory
-t használja a tesztosztályokban. Az alábbi példa a gyárat használja aIndexPageTests
osztályban:public class IndexPageTests : IClassFixture<CustomWebApplicationFactory<Program>> { private readonly HttpClient _client; private readonly CustomWebApplicationFactory<Program> _factory; public IndexPageTests( CustomWebApplicationFactory<Program> factory) { _factory = factory; _client = factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false }); } }
A mintaalkalmazás ügyfele úgy van konfigurálva, hogy megakadályozza a
HttpClient
átirányítások követését. Amint azt a Mock authentication szakasz későbbi szakasza ismerteti, ez lehetővé teszi a tesztek számára az alkalmazás első válaszának eredményének ellenőrzését. Az első válasz ezek közül sok tesztben egy átirányítás egy "Location
" fejléccel.Egy tipikus teszt a
HttpClient
és segédmetenek használatával dolgozza fel a kérést és a választ:[Fact] public async Task Post_DeleteAllMessagesHandler_ReturnsRedirectToRoot() { // Arrange var defaultPage = await _client.GetAsync("/"); var content = await HtmlHelpers.GetDocumentAsync(defaultPage); //Act var response = await _client.SendAsync( (IHtmlFormElement)content.QuerySelector("form[id='messages']"), (IHtmlButtonElement)content.QuerySelector("button[id='deleteAllBtn']")); // Assert Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode); Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); Assert.Equal("/", response.Headers.Location.OriginalString); }
A SUT-nak küldött POST-kéréseknek meg kell felelniük az alkalmazás adatvédelmi rendszerének automatikusan végrehajtott hamisítás elleni ellenőrzési folyamatának. A teszt POST-kérésének rendezéséhez a tesztalkalmazásnak a következőnek kell lennie:
- Kérjen egy kérelmet az oldalhoz.
- Elemezze az azonosítóhamisítás elleni cookie-t, és kérje le az érvényesítési jogkivonatot a választól.
- Végezze el a POST kérést az antiforgery cookie és az érvényesítési jogkivonattal.
A mintaalkalmazás SendAsync
segédmetódusai (Helpers/HttpClientExtensions.cs
) és a GetDocumentAsync
segédmetódus (Helpers/HtmlHelpers.cs
) az AngleSharp elemzővel kezelik az antiforgery-ellenőrzést az alábbi módszerekkel:
-
GetDocumentAsync
: Megkapja a HttpResponseMessage, és visszaad egyIHtmlDocument
.GetDocumentAsync
egy olyan gyárat használ, amely az eredeti alapján készít elő egyHttpResponseMessage
-t. További információért tekintse meg az AngleSharp dokumentációt. -
SendAsync
kiterjesztési metódusai aHttpClient
összállítanak egy HttpRequestMessage-t, és meghívják a SendAsync(HttpRequestMessage)-t, hogy kérelmeket küldjenek a SUT-nak. ASendAsync
túlterhelései elfogadják a HTML-űrlapot (IHtmlFormElement
) és a következőket:- Az űrlap Küldés gombja (
IHtmlElement
) - Űrlapértékek gyűjtése (
IEnumerable<KeyValuePair<string, string>>
) - Küldés gomb (
IHtmlElement
) és űrlapértékek (IEnumerable<KeyValuePair<string, string>>
)
- Az űrlap Küldés gombja (
AngleSharp egy harmadik féltől származó elemzési kódtár, amelyet bemutató célokra használnak, ebben a cikkben és a mintaalkalmazásban. Az AngleSharp nem támogatott vagy szükséges ASP.NET Core-alkalmazások integrációs teszteléséhez. Más elemzők is használhatók, például a Html Agility Pack (HAP). Egy másik módszer, ha olyan kódot írunk, amely közvetlenül kezeli az antiforgery rendszer kérés-ellenőrzési tokenjét és az antiforgery cookie-t. További információkért tekintse meg az antiforgery-ellenőrzésekről szóló cikket itt: AngleSharp vs Application Parts
,.
A EF-Core memóriabeli adatbázis-szolgáltató korlátozott és alapszintű teszteléshez használható, de a SQLite-szolgáltató a memóriabeli teszteléshez ajánlott.
Lásd: Indítás kiterjesztése indítási szűrőkkel, amely bemutatja, hogyan konfigurálhat köztes szoftvereket IStartupFilterhasználatával, ami akkor hasznos, ha egy teszthez egyéni szolgáltatásra vagy köztes szoftverre van szükség.
Az ügyfél testreszabása a WithWebHostBuilder használatával
Ha egy tesztmetóduson belül további konfigurációra van szükség, WithWebHostBuilder létrehoz egy új WebApplicationFactory
egy olyan IWebHostBuilder-vel, amelyet a konfiguráció további testreszab.
A mintakód meghívja WithWebHostBuilder
, hogy a konfigurált szolgáltatásokat tesztcsomókkal cserélje le. További információért és a példák használatáért lásd a Modellszolgáltatások injektálása rész ebben a cikkben.
A mintaalkalmazás Post_DeleteMessageHandler_ReturnsRedirectToRoot
tesztmetódusa a WithWebHostBuilder
használatát mutatja be. Ez a teszt egy rekord törlését hajtja végre az adatbázisban egy űrlapbeküldés SUT-ban való aktiválásával.
Mivel a IndexPageTests
osztály egy másik tesztje olyan műveletet hajt végre, amely törli az adatbázis összes rekordját, és a Post_DeleteMessageHandler_ReturnsRedirectToRoot
metódus előtt futhat, az adatbázist ebben a tesztmetódusban újból el kell végezni, hogy a rendszer egy rekordot adjon meg a SUT számára a törléshez. A SUT-ban a messages
űrlap első törlési gombjának kiválasztását a rendszer szimulálja a SUT-nak küldött kérésben:
[Fact]
public async Task Post_DeleteMessageHandler_ReturnsRedirectToRoot()
{
// Arrange
using (var scope = _factory.Services.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<ApplicationDbContext>();
Utilities.ReinitializeDbForTests(db);
}
var defaultPage = await _client.GetAsync("/");
var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
//Act
var response = await _client.SendAsync(
(IHtmlFormElement)content.QuerySelector("form[id='messages']"),
(IHtmlButtonElement)content.QuerySelector("form[id='messages']")
.QuerySelector("div[class='panel-body']")
.QuerySelector("button"));
// Assert
Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode);
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.Equal("/", response.Headers.Location.OriginalString);
}
Ügyfélbeállítások
A WebApplicationFactoryClientOptions példányok létrehozásakor az alapértelmezett beállításokat és az elérhető beállításokat a HttpClient
lapon tekinti meg.
Hozza létre a WebApplicationFactoryClientOptions
osztályt, és adja át a CreateClient() metódusnak:
public class IndexPageTests :
IClassFixture<CustomWebApplicationFactory<Program>>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory<Program>
_factory;
public IndexPageTests(
CustomWebApplicationFactory<Program> factory)
{
_factory = factory;
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
}
}
MEGJEGYZÉS: A HTTPS-átirányítási figyelmeztetések elkerülése érdekében, amikor a HTTPS-átirányító köztes réteget használja, állítsa be a BaseAddress = new Uri("https://localhost")
Makettszolgáltatások injektálása
A szolgáltatások felülírhatók egy tesztben a ConfigureTestServices hívásával a gazdagép építőn. A felülbírált szolgáltatások konkrét teszt hatóköréhez a WithWebHostBuilder metódus használható egy hostkészítő lekérésére. Ez a következő tesztekben látható:
- Get_QuoteService_ProvidesQuoteInPage
- Szerezd_MegGithubProfilOldalAmiElérhetEgyGithubFelhasználót
- Biztonságos_ElőoldalVisszaadvaHitelesítettFelhasználónak
A minta SUT tartalmaz egy hatókörű szolgáltatást, amely egy idézetet ad vissza. Az indexlap kérésekor az idézet az Index lap rejtett mezőjébe van beágyazva.
Services/IQuoteService.cs
:
public interface IQuoteService
{
Task<string> GenerateQuote();
}
Services/QuoteService.cs
:
// Quote ©1975 BBC: The Doctor (Tom Baker); Dr. Who: Planet of Evil
// https://www.bbc.co.uk/programmes/p00pyrx6
public class QuoteService : IQuoteService
{
public Task<string> GenerateQuote()
{
return Task.FromResult<string>(
"Come on, Sarah. We've an appointment in London, " +
"and we're already 30,000 years late.");
}
}
Program.cs
:
services.AddScoped<IQuoteService, QuoteService>();
Pages/Index.cshtml.cs
:
public class IndexModel : PageModel
{
private readonly ApplicationDbContext _db;
private readonly IQuoteService _quoteService;
public IndexModel(ApplicationDbContext db, IQuoteService quoteService)
{
_db = db;
_quoteService = quoteService;
}
[BindProperty]
public Message Message { get; set; }
public IList<Message> Messages { get; private set; }
[TempData]
public string MessageAnalysisResult { get; set; }
public string Quote { get; private set; }
public async Task OnGetAsync()
{
Messages = await _db.GetMessagesAsync();
Quote = await _quoteService.GenerateQuote();
}
Pages/Index.cs
:
<input id="quote" type="hidden" value="@Model.Quote">
A SUT-alkalmazás futtatásakor a következő korrektúra jön létre:
<input id="quote" type="hidden" value="Come on, Sarah. We've an appointment in
London, and we're already 30,000 years late.">
A szolgáltatás és az injektálás teszteléséhez integrációs tesztben a teszt egy ál-szolgáltatást injektál a SUT-ba. A modellszolgáltatás lecseréli az alkalmazás QuoteService
-t a tesztalkalmazás által biztosított, TestQuoteService
nevű szolgáltatásra.
IntegrationTests.IndexPageTests.cs
:
// Quote ©1975 BBC: The Doctor (Tom Baker); Pyramids of Mars
// https://www.bbc.co.uk/programmes/p00pys55
public class TestQuoteService : IQuoteService
{
public Task<string> GenerateQuote()
{
return Task.FromResult(
"Something's interfering with time, Mr. Scarman, " +
"and time is my business.");
}
}
ConfigureTestServices
van meghívva, és a hatókörön belüli szolgáltatás regisztrálva van:
[Fact]
public async Task Get_QuoteService_ProvidesQuoteInPage()
{
// Arrange
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddScoped<IQuoteService, TestQuoteService>();
});
})
.CreateClient();
//Act
var defaultPage = await client.GetAsync("/");
var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
var quoteElement = content.QuerySelector("#quote");
// Assert
Assert.Equal("Something's interfering with time, Mr. Scarman, " +
"and time is my business.", quoteElement.Attributes["value"].Value);
}
A teszt végrehajtása során létrehozott jelölések a TestQuoteService
által megadott idézőjelszöveget tükrözik, ezért az állítás sikeres.
<input id="quote" type="hidden" value="Something's interfering with time,
Mr. Scarman, and time is my business.">
Szimulált hitelesítés
Az AuthTests
osztály tesztjei ellenőrzik, hogy egy biztonságos végpont:
- Átirányít egy nem hitelesített felhasználót az alkalmazás bejelentkezési oldalára.
- Egy hitelesített felhasználó tartalmát adja vissza.
A SUT-ban a /SecurePage
lap a AuthorizePage konvenciót használja, hogy egy AuthorizeFilter-t alkalmazzon a lapra. További információkért lásd a Razor oldalak engedélyezési konvencióit.
services.AddRazorPages(options =>
{
options.Conventions.AuthorizePage("/SecurePage");
});
A Get_SecurePageRedirectsAnUnauthenticatedUser
tesztben egy WebApplicationFactoryClientOptions úgy van beállítva, hogy letiltsa az átirányításokat azáltal, hogy AllowAutoRedirect-t false
-ra állítja.
[Fact]
public async Task Get_SecurePageRedirectsAnUnauthenticatedUser()
{
// Arrange
var client = _factory.CreateClient(
new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
// Act
var response = await client.GetAsync("/SecurePage");
// Assert
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.StartsWith("http://localhost/Identity/Account/Login",
response.Headers.Location.OriginalString);
}
Az ügyfél átirányításának megakadályozásával a következő ellenőrzéseket hajthatja végre:
- A SUT által visszaadott állapotkód a várt HttpStatusCode.Redirect eredményen ellenőrizhető, nem pedig a bejelentkezési lapra való átirányítás utáni végleges állapotkóddal, amely HttpStatusCode.OK.
- A válaszfejlécek közül a
Location
fejléc értékét ellenőrzik annak megállapítására, hogyhttp://localhost/Identity/Account/Login
-el kezdődik-e, nem pedig a végleges bejelentkezési oldal válaszát, ahol aLocation
fejléc nem lenne jelen.
A tesztalkalmazás szimulálhat egy AuthenticationHandler<TOptions>ConfigureTestServices-ben a hitelesítés és az engedélyezés szempontjainak teszteléséhez. Egy minimális forgatókönyv AuthenticateResult.Success-t ad vissza.
public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger, UrlEncoder encoder)
: base(options, logger, encoder)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var claims = new[] { new Claim(ClaimTypes.Name, "Test user") };
var identity = new ClaimsIdentity(claims, "Test");
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, "TestScheme");
var result = AuthenticateResult.Success(ticket);
return Task.FromResult(result);
}
}
A TestAuthHandler
egy felhasználó hitelesítésére hívják meg, amikor a hitelesítési séma TestScheme
van beállítva, ahol a AddAuthentication
regisztrálva van a ConfigureTestServices
-ra. Fontos, hogy a TestScheme
séma megfeleljen az alkalmazás által várt sémának. Ellenkező esetben a hitelesítés nem működik.
[Fact]
public async Task Get_SecurePageIsReturnedForAnAuthenticatedUser()
{
// Arrange
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddAuthentication(defaultScheme: "TestScheme")
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
"TestScheme", options => { });
});
})
.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false,
});
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(scheme: "TestScheme");
//Act
var response = await client.GetAsync("/SecurePage");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
A WebApplicationFactoryClientOptions
kapcsolatos további információkért lásd a Ügyfélbeállítások című szakaszt.
A köztes szoftver hitelesítésének alapszintű tesztjei
A Köztes szoftver hitelesítésének alapszintű teszteléséhez tekintse meg ezt a GitHub-adattárat. A tesztforgatókönyvre jellemző tesztkiszolgálót tartalmaz.
A környezet beállítása
Állítsa be a környezetet az egyéni alkalmazás-előállítóban:
public class CustomWebApplicationFactory<TProgram>
: WebApplicationFactory<TProgram> where TProgram : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var dbContextDescriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbContextOptions<ApplicationDbContext>));
services.Remove(dbContextDescriptor);
var dbConnectionDescriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbConnection));
services.Remove(dbConnectionDescriptor);
// Create open SqliteConnection so EF won't automatically close it.
services.AddSingleton<DbConnection>(container =>
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
return connection;
});
services.AddDbContext<ApplicationDbContext>((container, options) =>
{
var connection = container.GetRequiredService<DbConnection>();
options.UseSqlite(connection);
});
});
builder.UseEnvironment("Development");
}
}
Hogyan következtet a tesztinfrastruktúra az alkalmazás tartalomgyökerének elérési útjára?
A WebApplicationFactory
konstruktor meghatározza az alkalmazás tartalom gyökér elérési útját úgy, hogy a WebApplicationFactoryContentRootAttribute szerelvény TEntryPoint
kulccsal a System.Reflection.Assembly.FullName
-t keres az integrációs teszteket tartalmazó szerelvényen. Ha nem található a megfelelő kulccsal rendelkező attribútum, WebApplicationFactory
visszatér a megoldásfájl (.sln) kereséséhez, és hozzáadja a TEntryPoint
szerelvény nevét a megoldáskönyvtárhoz. Az alkalmazás gyökérkönyvtára (a tartalom gyökérútvonala) a nézetek és tartalomfájlok felderítésére szolgál.
Árnyékmásolás letiltása
Az árnyékmásolás miatt a tesztek a kimeneti könyvtártól eltérő könyvtárban futnak. Ha a tesztek a fájlok Assembly.Location
-hoz viszonyított betöltésére támaszkodnak, és problémákat tapasztalunk, előfordulhat, hogy le kell tiltani az árnyékmásolást.
Ha le szeretné tiltani az árnyékmásolást az xUnit használatakor, hozzon létre egy xunit.runner.json
fájlt a tesztprojekt könyvtárában a megfelelő konfigurációs beállítással:
{
"shadowCopy": false
}
Objektumok ártalmatlanítása
A IClassFixture
implementáció tesztjeinek végrehajtása után TestServer és HttpClient megsemmisülnek, amikor az xUnit megsemmisíti a WebApplicationFactory
-at. Ha a fejlesztő által példányosított objektumok ártalmatlanítást igényelnek, azokat a IClassFixture
implementációban kell megsemmisíteni. További információért lásd: Dispose metódus implementálása.
Integrációs tesztek mintája
A mintaalkalmazás két alkalmazásból áll:
Alkalmazás | Projektmappa | Leírás |
---|---|---|
Üzenetküldő alkalmazás (a SUT) | src/RazorPagesProject |
Lehetővé teszi a felhasználó számára, hogy hozzáadjon, töröljön egyet, törölje az összeset, és elemezze az üzeneteket. |
Alkalmazás tesztelése | tests/RazorPagesProject.Tests |
A SUT integrációs tesztelésére szolgál. |
A tesztek egy IDE beépített tesztfunkcióival futtathatók, például Visual Studio. Ha Visual Studio Code vagy parancssort használ, hajtsa végre a következő parancsot egy parancssorban a tests/RazorPagesProject.Tests
könyvtárban:
dotnet test
Üzenetküldő alkalmazás (SUT) szervezete
A SUT egy Razor Pages üzenetrendszer, amely a következő jellemzőkkel rendelkezik:
- Az alkalmazás indexoldala (
Pages/Index.cshtml
ésPages/Index.cshtml.cs
) felhasználói felületi és lapmodellezési módszereket biztosít az üzenetek hozzáadásának, törlésének és elemzésének szabályozásához (átlagos szavak üzenetenként). - Az üzeneteket a
Message
osztály (Data/Message.cs
) írja le két tulajdonságokkal:Id
(kulcs) ésText
(üzenet). AText
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 egy adatelérési réteget (DAL) tartalmaz az adatbázis környezeti osztályában,
AppDbContext
(Data/AppDbContext.cs
). - Ha az adatbázis üres az alkalmazás indításakor, az üzenettároló három üzenettel lesz inicializálva.
- Az alkalmazás tartalmaz egy
/SecurePage
, amelyet csak hitelesített felhasználó érhet el.
†Az EF-cikk, Az InMemorytesztelése című cikk 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 az alkalmazás nem használja az adattár mintát, és nem hatékony példa a Munkaegység (UoW) mintára, a Razor Pages támogatja ezeket a fejlesztési mintákat. További információ: Az infrastruktúra perzisztenciaréteg tervezése és Tesztvezérlő logika (a minta implementálja az adattár mintát).
Tesztalkalmazás szervezése
A tesztalkalmazás egy konzolalkalmazás a tests/RazorPagesProject.Tests
könyvtárban.
Alkalmazáskönyvtár tesztelése | Leírás |
---|---|
AuthTests |
A következő tesztelési módszereket tartalmazza:
|
BasicTests |
Az útválasztás és a tartalomtípus tesztelési módszerét tartalmazza. |
IntegrationTests |
Az Index lap integrációs tesztjeit tartalmazza egyéni WebApplicationFactory osztály használatával. |
Helpers/Utilities |
|
A tesztelési keretrendszer xUnit. Az integrációs teszteket a Microsoft.AspNetCore.TestHostsegítségével hajtják végre, amely magában foglalja a TestServer-et. Mivel a Microsoft.AspNetCore.Mvc.Testing
csomag a tesztgazda és a tesztkiszolgáló konfigurálására szolgál, a TestHost
és TestServer
csomagok nem igényelnek közvetlen csomaghivatkozásokat a tesztalkalmazás projektfájljában vagy fejlesztői konfigurációjában a tesztalkalmazásban.
Az integrációs tesztek általában kis adatkészletet igényelnek az adatbázisban a teszt végrehajtása előtt. A törlési teszt például egy adatbázisrekord törlését kéri, ezért az adatbázisnak legalább egy rekorddal kell rendelkeznie ahhoz, hogy a törlési kérelem sikeres legyen.
A mintaalkalmazás három üzenettel tölti fel az adatbázist a Utilities.cs
-ben, amelyeket a tesztek futtatásakor használhatnak.
public static void InitializeDbForTests(ApplicationDbContext db)
{
db.Messages.AddRange(GetSeedingMessages());
db.SaveChanges();
}
public static void ReinitializeDbForTests(ApplicationDbContext db)
{
db.Messages.RemoveRange(db.Messages);
InitializeDbForTests(db);
}
public static List<Message> GetSeedingMessages()
{
return new List<Message>()
{
new Message(){ Text = "TEST RECORD: You're standing on my scarf." },
new Message(){ Text = "TEST RECORD: Would you like a jelly baby?" },
new Message(){ Text = "TEST RECORD: To the rational mind, " +
"nothing is inexplicable; only unexplained." }
};
}
A SUT adatbázis-környezetét regisztrálták Program.cs
-ben. A tesztalkalmazás builder.ConfigureServices
visszahívása az alkalmazás lesz végrehajtva. Ha másik adatbázist szeretne használni a tesztekhez, az alkalmazás adatbázis-környezetét le kell cserélni a builder.ConfigureServices
-ban. A további információkért lásd a WebApplicationFactory testreszabása szakaszt.
További erőforrások
- egységtesztek
- Razor Lapok egységtesztjei ASP.NET Core
- ASP.NET Core köztes szoftver
- Tesztvezérlő logika az ASP.NET Core-ban
- Köztes szoftver hitelesítésének alapszintű tesztjei
Ez a cikk feltételezi az egységtesztek alapszintű megértését. Ha nem ismeri a tesztelési fogalmakat, tekintse meg a .NET Core és a .NET Standard Egységtesztelés című cikket és annak csatolt tartalmát.
Mintakód megtekintése vagy letöltése (hogyan kell letölteni)
A mintaalkalmazás egy Razor Pages-alkalmazás, és feltételezi, hogy alapszintű ismeretekkel rendelkezik a Razor Pages-ről. Ha nem ismeri a Razor oldalak tartalmát, olvassa el a következő cikkeket.
Az SLA-kteszteléséhez olyan eszközt ajánlunk, mint például a .NET-Playwright, amely automatizálhatja a böngészőt.
Bevezetés az integrációs tesztekbe
Az integrációs tesztek az alkalmazás összetevőit szélesebb szinten értékelik ki, mint egységtesztek. Az egységtesztek izolált szoftverösszetevők, például az egyes osztálymódszerek tesztelésére szolgálnak. Az integrációs tesztek megerősítik, hogy két vagy több alkalmazásösszetevő együttműködik a várt eredmény érdekében, beleértve a kérések teljes feldolgozásához szükséges összes összetevőt is.
Ezek a szélesebb körű tesztek az alkalmazás infrastruktúrájának és teljes keretrendszerének tesztelésére szolgálnak, gyakran a következő összetevőket is beleértve:
- Adatbázis
- Fájlrendszer
- Hálózati berendezések
- Kérelem-válasz folyamat
Az egységtesztek az infrastruktúra-összetevők helyett létrehozott összetevőket használnak, más néven hamis vagy szimulált objektumokat.
Az egységtesztekkel ellentétben az integrációs tesztek:
- Használja azokat a tényleges összetevőket, amelyeket az alkalmazás éles környezetben használ.
- További kód- és adatfeldolgozást igényel.
- A futtatás hosszabb időt vesz igénybe.
Ezért az integrációs tesztek használatának korlátozása a legfontosabb infrastruktúra-forgatókönyvekre. Ha egy viselkedést egységteszt vagy integrációs teszt használatával lehet tesztelni, válassza ki az egységtesztet.
Az integrációs tesztek megvitatása során a tesztelt projektet gyakran nevezik System Under Test, vagy röviden "SUT" néven. A cikk során a "SUT" a tesztelt ASP.NET Core-alkalmazásra hivatkozik.
Ne írjon integrációs teszteket az adatbázisok és fájlrendszerek minden permutációs adat- és fájlhozzáféréshez. Függetlenül attól, hogy az alkalmazások hány helyen használják az adatbázisokat és a fájlrendszereket, az olvasási, írási, frissítési és törlési integrációs tesztek koncentrált készlete általában képes megfelelően tesztelni az adatbázis- és fájlrendszer-összetevőket. Használjon egységteszteket az ezen összetevőkkel interakcióba lépő metóduslogika rutintesztjeihez. Az egységtesztekben az infrastruktúra hamis vagy hamisított használata gyorsabb tesztvégrehajtást eredményez.
ASP.NET Core integrációs tesztek
A ASP.NET Core integrációs tesztjeihez a következők szükségesek:
- A tesztprojektek a teszteket tartalmazzák és hajtják végre. A tesztprojekt hivatkozással rendelkezik a SUT-ra.
- A tesztprojekt létrehoz egy tesztwebhely-gazdagépet a SUT számára, és egy tesztkiszolgáló-ügyféllel kezeli a kérelmeket és a válaszokat a SUT-val.
- A tesztfuttató a tesztek végrehajtására és a teszteredmények jelentésére szolgál.
Az integrációs tesztek olyan események sorozatát követik, amelyek magukban foglalják a szokásos Előkészület, Végrehajtásés Ellenőrzés tesztelési lépéseket.
- Az SUT webhoszt konfigurálva van.
- Létrejön egy tesztkiszolgáló-ügyfél, amely kéréseket küld az alkalmazásnak.
- A Arrange tesztelési lépés végrehajtása: A tesztalkalmazás előkészít egy kérést.
- A törvény tesztlépés végrehajtása: Az ügyfél elküldi a kérést, és megkapja a választ.
- A ellenőrzési tesztlépést végrehajtják: A tényleges választ a rendszer a várt válasz alapján érvényesíti, és érvényesnek nyilvánítja (), vagy sikertelennek ().
- A folyamat az összes teszt végrehajtásáig folytatódik.
- A teszteredmények jelentésre kerültek.
A teszt webgazda általában eltérő konfigurációval van beállítva, mint az alkalmazás normál webgazdája a tesztek futtatásához. Előfordulhat például, hogy a tesztekhez egy másik adatbázist vagy különböző alkalmazásbeállításokat használnak.
Az infrastruktúra-összetevőket, például a tesztweb-gazdagépet és a memóriabeli tesztkiszolgálót (TestServer) a Microsoft.AspNetCore.Mvc.Testing csomag biztosítja vagy felügyeli. A csomag használata leegyszerűsíti a tesztek létrehozását és végrehajtását.
A Microsoft.AspNetCore.Mvc.Testing
csomag a következő feladatokat kezeli:
- Másolja a függőségi fájlt (
.deps
) a SUT-ból a tesztprojektbin
könyvtárába. - A tartalomgyökerét-re a SUT projektgyökeréhez állítja, hogy a statikus fájlok és a lapok/nézetek megtalálhatók legyenek a tesztek végrehajtásakor.
- A WebApplicationFactory osztály gördülékenyebbé teszi a SUT
TestServer
használatával történő rendszerindítását.
Az egységtesztek dokumentációja ismerteti a tesztprojektek és tesztfuttatók beállításának módját, valamint a tesztek futtatásának részletes utasításait, valamint a tesztek és tesztosztályok elnevezésére vonatkozó javaslatokat.
Egységtesztek elkülönítése az integrációs tesztektől különböző projektekre. A tesztek elkülönítése:
- Segít biztosítani, hogy az infrastruktúra tesztelési összetevői véletlenül ne legyenek belefoglalva az egységtesztekbe.
- Lehetővé teszi a tesztkészlet futtatásának szabályozását.
Gyakorlatilag nincs különbség a Razor Pages-alkalmazások és az MVC-alkalmazások tesztjeinek konfigurációja között. Az egyetlen különbség a tesztek elnevezésében van. Egy Razor Pages-alkalmazásban az oldalvégpontok tesztjeit általában az oldalmodell-osztályról nevezik el (például IndexPageTests
az indexlap összetevőintegrációjának teszteléséhez). Az MVC-alkalmazásokban a tesztek általában vezérlőosztályok szerint vannak rendszerezve, és az általuk tesztelt vezérlőkről nevezik el őket (például HomeControllerTests
a Home vezérlő összetevőintegrációjának teszteléséhez).
Alkalmazás előfeltételeinek tesztelése
A tesztprojektnek a következőnek kell lennie:
- Hivatkozzon a
Microsoft.AspNetCore.Mvc.Testing
csomagra. - Adja meg a webes SDK-t a projektfájlban (
<Project Sdk="Microsoft.NET.Sdk.Web">
).
Ezek az előfeltételek láthatók a mintaalkalmazás-ban. Vizsgálja meg a tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj
fájlt. A mintaalkalmazás az xUnit tesztelési keretrendszert és a AngleSharp elemzőtárat használja, így a mintaalkalmazás a következőre is hivatkozik:
A xunit.runner.visualstudio
2.4.2-es vagy újabb verzióját használó alkalmazásokban a tesztprojektnek hivatkoznia kell a Microsoft.NET.Test.Sdk
csomagra.
Az Entity Framework Core a tesztekben is használatos. Tekintse meg a projektfájlt a GitHub.
SUT-környezet
Ha a SUT környezet nincs beállítva, a környezet alapértelmezés szerint a Fejlesztési környezet.
Alapszintű tesztek az alapértelmezett WebApplicationFactory használatával
Tegye közzé az implicit módon definiált Program
osztályt a tesztprojektben az alábbi műveletek egyikével:
Belső típusok kitétele a webalkalmazásból a tesztprojekt számára. Ez a SUT-projekt fájljában (
.csproj
) végezhető el:<ItemGroup> <InternalsVisibleTo Include="MyTestProject" /> </ItemGroup>
Tegye nyilvánossá a
Program
osztályt egy részleges osztály deklarációval:var builder = WebApplication.CreateBuilder(args); // ... Configure services, routes, etc. app.Run(); + public partial class Program { }
A mintaalkalmazás a
Program
részleges osztály megközelítést használja.
A WebApplicationFactory<TEntryPoint>-t használjuk egy TestServer létrehozásához az integrációs tesztekhez.
TEntryPoint
a SUT belépési pontosztálya, általában Program.cs
.
A tesztosztályok implementálnak egy osztály-fix technikai felületet (IClassFixture
), amely jelzi, hogy az osztály teszteket tartalmaz, és megosztott objektumpéldányokat biztosít az osztály tesztjei során.
Az alábbi tesztosztály BasicTests
a WebApplicationFactory
használatával végzi a SUT inicializálását, és biztosít egy HttpClient-t a Get_EndpointsReturnSuccessAndCorrectContentType
tesztmetódus számára. A metódus ellenőrzi, hogy a válasz állapotkódja sikeres-e (200-299), és a Content-Type
fejléc több alkalmazásoldalon text/html; charset=utf-8
.
CreateClient() létrehoz egy HttpClient
-példányt, amely automatikusan követi az átirányításokat és kezeli a cookie-kat.
public class BasicTests
: IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
public BasicTests(WebApplicationFactory<Program> factory)
{
_factory = factory;
}
[Theory]
[InlineData("/")]
[InlineData("/Index")]
[InlineData("/About")]
[InlineData("/Privacy")]
[InlineData("/Contact")]
public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.Equal("text/html; charset=utf-8",
response.Content.Headers.ContentType.ToString());
}
}
[TestClass]
public class BasicTests
{
private static CustomWebApplicationFactory<Program> _factory;
[ClassInitialize]
public static void AssemblyInitialize(TestContext _)
{
_factory = new CustomWebApplicationFactory<Program>();
}
[ClassCleanup(ClassCleanupBehavior.EndOfClass)]
public static void AssemblyCleanup(TestContext _)
{
_factory.Dispose();
}
[TestMethod]
[DataRow("/")]
[DataRow("/Index")]
[DataRow("/About")]
[DataRow("/Privacy")]
[DataRow("/Contact")]
public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.AreEqual("text/html; charset=utf-8",
response.Content.Headers.ContentType.ToString());
}
}
public class BasicTests
{
private CustomWebApplicationFactory<Program>
_factory;
[SetUp]
public void SetUp()
{
_factory = new CustomWebApplicationFactory<Program>();
}
[TearDown]
public void TearDown()
{
_factory.Dispose();
}
[DatapointSource]
public string[] values = ["/", "/Index", "/About", "/Privacy", "/Contact"];
[Theory]
public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.That(response.Content.Headers.ContentType.ToString(), Is.EqualTo("text/html; charset=utf-8"));
}
}
Alapértelmezés szerint a nem alapvető cookie-k nem maradnak meg a kérések között, ha engedélyezve van az általános adatvédelmi rendelet hozzájárulási szabályzata. A nem alapvető cookie-k( például a TempData-szolgáltató által használt) megőrzése érdekében jelölje meg őket alapvetőként a tesztek során. A cookie alapvetőként való megjelölésére vonatkozó utasításokért lásd: Alapvető cookie-k.
AngleSharp és Application Parts
az antiforgery-ellenőrzésekhez
Ez a cikk a AngleSharp elemzőt használja az antiforgery-ellenőrzések kezelésére oldalak betöltése és a HTML elemzése révén. A vezérlő végpontjainak és a Razor oldalak nézeteinek részletesebb teszteléséhez, anélkül, hogy törődne azzal, hogyan jelennek meg a böngészőben, fontolja meg a Application Parts
használatát. Az Alkalmazásrészek megközelítés egy vezérlőt vagy Razor lapot injektál az alkalmazásba, amellyel JSON-kéréseket lehet küldeni a szükséges értékek lekéréséhez. További információért lásd a blogot: Integrációs tesztelés ASP.NET Core erőforrások Antiforgery-vel történő védelme az Alkalmazás Részei segítségével és a kapcsolódó GitHub-adattár szerzője: Martin Costello.
A WebApplicationFactory testreszabása
A webtárhely konfigurációja a tesztosztályoktól függetlenül hozható létre, úgy, hogy a WebApplicationFactory<TEntryPoint>-ból örökölt egy vagy több egyéni gyárat készítünk.
Örököljön
WebApplicationFactory
és bírálja felül ConfigureWebHost. A IWebHostBuilder lehetővé teszi a szolgáltatásgyűjtemény konfigurálásátIWebHostBuilder.ConfigureServices
public class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<TProgram> where TProgram : class { protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureServices(services => { var dbContextDescriptor = services.SingleOrDefault( d => d.ServiceType == typeof(IDbContextOptionsConfiguration<ApplicationDbContext>)); services.Remove(dbContextDescriptor); var dbConnectionDescriptor = services.SingleOrDefault( d => d.ServiceType == typeof(DbConnection)); services.Remove(dbConnectionDescriptor); // Create open SqliteConnection so EF won't automatically close it. services.AddSingleton<DbConnection>(container => { var connection = new SqliteConnection("DataSource=:memory:"); connection.Open(); return connection; }); services.AddDbContext<ApplicationDbContext>((container, options) => { var connection = container.GetRequiredService<DbConnection>(); options.UseSqlite(connection); }); }); builder.UseEnvironment("Development"); } }
public class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<TProgram> where TProgram : class { protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureServices(services => { var dbContextDescriptor = services.SingleOrDefault( d => d.ServiceType == typeof(IDbContextOptionsConfiguration<ApplicationDbContext>)); services.Remove(dbContextDescriptor); var dbConnectionDescriptor = services.SingleOrDefault( d => d.ServiceType == typeof(DbConnection)); services.Remove(dbConnectionDescriptor); // Create open SqliteConnection so EF won't automatically close it. services.AddSingleton<DbConnection>(container => { var connection = new SqliteConnection("DataSource=:memory:"); connection.Open(); return connection; }); services.AddDbContext<ApplicationDbContext>((container, options) => { var connection = container.GetRequiredService<DbConnection>(); options.UseSqlite(connection); }); }); builder.UseEnvironment("Development"); } }
public class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<TProgram> where TProgram : class { protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureServices(services => { var dbContextDescriptor = services.SingleOrDefault( d => d.ServiceType == typeof(IDbContextOptionsConfiguration<ApplicationDbContext>)); services.Remove(dbContextDescriptor); var dbConnectionDescriptor = services.SingleOrDefault( d => d.ServiceType == typeof(DbConnection)); services.Remove(dbConnectionDescriptor); // Create open SqliteConnection so EF won't automatically close it. services.AddSingleton<DbConnection>(container => { var connection = new SqliteConnection("DataSource=:memory:"); connection.Open(); return connection; }); services.AddDbContext<ApplicationDbContext>((container, options) => { var connection = container.GetRequiredService<DbConnection>(); options.UseSqlite(connection); }); }); builder.UseEnvironment("Development"); } }
Az adatbázis-vetést a mintaalkalmazásban a
InitializeDbForTests
metódus végzi. A módszert az integrációs tesztek mintájában ismertetjük: Az alkalmazás szervezetének tesztelése szakasz.A SUT adatbázis-környezetét regisztrálták
Program.cs
-ben. A tesztalkalmazásbuilder.ConfigureServices
visszahívása az alkalmazás lesz végrehajtva. Ha más adatbázist szeretne használni a tesztekhez, mint az alkalmazás adatbázisa, az alkalmazás adatbázis-környezetét abuilder.ConfigureServices
adatbázis-környezetre kell cserélni.A mintaalkalmazás megkeresi az adatbázis-környezet szolgáltatásleíróját, és a leíró használatával eltávolítja a szolgáltatásregisztrációt. A gyár ezután hozzáad egy új
ApplicationDbContext
, amely memórián belüli adatbázist használ a tesztekhez.Ha másik adatbázishoz szeretne csatlakozni, módosítsa a
DbConnection
. SQL Server tesztadatbázis használata:- A projektfájlban hivatkozzon a
Microsoft.EntityFrameworkCore.SqlServer
NuGet-csomagra. - Hívja
UseInMemoryDatabase
.
- A projektfájlban hivatkozzon a
Az egyéni
CustomWebApplicationFactory
-t használja a tesztosztályokban. Az alábbi példa a gyárat használja aIndexPageTests
osztályban:public class IndexPageTests : IClassFixture<CustomWebApplicationFactory<Program>> { private readonly HttpClient _client; private readonly CustomWebApplicationFactory<Program> _factory; public IndexPageTests( CustomWebApplicationFactory<Program> factory) { _factory = factory; _client = factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false }); }
[TestClass] public class IndexPageTests { private static HttpClient _client; private static CustomWebApplicationFactory<Program> _factory; [ClassInitialize] public static void AssemblyInitialize(TestContext _) { _factory = new CustomWebApplicationFactory<Program>(); _client = _factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false }); } [ClassCleanup(ClassCleanupBehavior.EndOfClass)] public static void AssemblyCleanup(TestContext _) { _factory.Dispose(); }
public class IndexPageTests { private HttpClient _client; private CustomWebApplicationFactory<Program> _factory; [SetUp] public void SetUp() { _factory = new CustomWebApplicationFactory<Program>(); _client = _factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false }); } [TearDown] public void TearDown() { _factory.Dispose(); _client.Dispose(); }
A mintaalkalmazás ügyfele úgy van konfigurálva, hogy megakadályozza a
HttpClient
átirányítások követését. Amint azt a Mock authentication szakasz későbbi szakasza ismerteti, ez lehetővé teszi a tesztek számára az alkalmazás első válaszának eredményének ellenőrzését. Az első válasz ezek közül sok tesztben egy átirányítás egy "Location
" fejléccel.Egy tipikus teszt a
HttpClient
és segédmetenek használatával dolgozza fel a kérést és a választ:[Fact] public async Task Post_DeleteAllMessagesHandler_ReturnsRedirectToRoot() { // Arrange var defaultPage = await _client.GetAsync("/"); var content = await HtmlHelpers.GetDocumentAsync(defaultPage); //Act var response = await _client.SendAsync( (IHtmlFormElement)content.QuerySelector("form[id='messages']"), (IHtmlButtonElement)content.QuerySelector("button[id='deleteAllBtn']")); // Assert Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode); Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); Assert.Equal("/", response.Headers.Location.OriginalString); }
[TestMethod] public async Task Post_DeleteAllMessagesHandler_ReturnsRedirectToRoot() { // Arrange var defaultPage = await _client.GetAsync("/"); var content = await HtmlHelpers.GetDocumentAsync(defaultPage); //Act var response = await _client.SendAsync( (IHtmlFormElement)content.QuerySelector("form[id='messages']"), (IHtmlButtonElement)content.QuerySelector("button[id='deleteAllBtn']")); // Assert Assert.AreEqual(HttpStatusCode.OK, defaultPage.StatusCode); Assert.AreEqual(HttpStatusCode.Redirect, response.StatusCode); Assert.AreEqual("/", response.Headers.Location.OriginalString); }
[Test] public async Task Post_DeleteAllMessagesHandler_ReturnsRedirectToRoot() { // Arrange var defaultPage = await _client.GetAsync("/"); var content = await HtmlHelpers.GetDocumentAsync(defaultPage); //Act var response = await _client.SendAsync( (IHtmlFormElement)content.QuerySelector("form[id='messages']"), (IHtmlButtonElement)content.QuerySelector("button[id='deleteAllBtn']")); // Assert Assert.That(defaultPage.StatusCode, Is.EqualTo(HttpStatusCode.OK)); Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Redirect)); Assert.That(response.Headers.Location.OriginalString, Is.EqualTo("/")); }
A SUT-nak küldött POST-kéréseknek meg kell felelniük az alkalmazás adatvédelmi rendszerének automatikusan végrehajtott hamisítás elleni ellenőrzési folyamatának. A teszt POST-kérésének rendezéséhez a tesztalkalmazásnak a következőnek kell lennie:
- Kérjen egy kérelmet az oldalhoz.
- Elemezze az azonosítóhamisítás elleni cookie-t, és kérje le az érvényesítési jogkivonatot a választól.
- Végezze el a POST kérést az antiforgery cookie és az érvényesítési jogkivonattal.
A mintaalkalmazás SendAsync
segédmetódusai (Helpers/HttpClientExtensions.cs
) és a GetDocumentAsync
segédmetódus (Helpers/HtmlHelpers.cs
) az AngleSharp elemzővel kezelik az antiforgery-ellenőrzést az alábbi módszerekkel:
-
GetDocumentAsync
: Megkapja a HttpResponseMessage, és visszaad egyIHtmlDocument
.GetDocumentAsync
egy olyan gyárat használ, amely az eredeti alapján készít elő egyHttpResponseMessage
-t. További információért tekintse meg az AngleSharp dokumentációt. -
SendAsync
kiterjesztési metódusai aHttpClient
összállítanak egy HttpRequestMessage-t, és meghívják a SendAsync(HttpRequestMessage)-t, hogy kérelmeket küldjenek a SUT-nak. ASendAsync
túlterhelései elfogadják a HTML-űrlapot (IHtmlFormElement
) és a következőket:- Az űrlap Küldés gombja (
IHtmlElement
) - Űrlapértékek gyűjtése (
IEnumerable<KeyValuePair<string, string>>
) - Küldés gomb (
IHtmlElement
) és űrlapértékek (IEnumerable<KeyValuePair<string, string>>
)
- Az űrlap Küldés gombja (
AngleSharp egy harmadik féltől származó elemzési kódtár, amelyet bemutató célokra használnak, ebben a cikkben és a mintaalkalmazásban. Az AngleSharp nem támogatott vagy szükséges ASP.NET Core-alkalmazások integrációs teszteléséhez. Más elemzők is használhatók, például a Html Agility Pack (HAP). Egy másik módszer, ha olyan kódot írunk, amely közvetlenül kezeli az antiforgery rendszer kérés-ellenőrzési tokenjét és az antiforgery cookie-t. További információkért tekintse meg az antiforgery-ellenőrzésekről szóló cikket itt: AngleSharp vs Application Parts
,.
A EF-Core memóriabeli adatbázis-szolgáltató korlátozott és alapszintű teszteléshez használható, de a SQLite-szolgáltató a memóriabeli teszteléshez ajánlott.
Lásd: Indítás kiterjesztése indítási szűrőkkel, amely bemutatja, hogyan konfigurálhat köztes szoftvereket IStartupFilterhasználatával, ami akkor hasznos, ha egy teszthez egyéni szolgáltatásra vagy köztes szoftverre van szükség.
Az ügyfél testreszabása a WithWebHostBuilder használatával
Ha egy tesztmetóduson belül további konfigurációra van szükség, WithWebHostBuilder létrehoz egy új WebApplicationFactory
egy olyan IWebHostBuilder-vel, amelyet a konfiguráció további testreszab.
A mintakód meghívja WithWebHostBuilder
, hogy a konfigurált szolgáltatásokat tesztcsomókkal cserélje le. További információért és a példák használatáért lásd a Modellszolgáltatások injektálása rész ebben a cikkben.
A mintaalkalmazás Post_DeleteMessageHandler_ReturnsRedirectToRoot
tesztmetódusa a WithWebHostBuilder
használatát mutatja be. Ez a teszt egy rekord törlését hajtja végre az adatbázisban egy űrlapbeküldés SUT-ban való aktiválásával.
Mivel a IndexPageTests
osztály egy másik tesztje olyan műveletet hajt végre, amely törli az adatbázis összes rekordját, és a Post_DeleteMessageHandler_ReturnsRedirectToRoot
metódus előtt futhat, az adatbázist ebben a tesztmetódusban újból el kell végezni, hogy a rendszer egy rekordot adjon meg a SUT számára a törléshez. A SUT-ban a messages
űrlap első törlési gombjának kiválasztását a rendszer szimulálja a SUT-nak küldött kérésben:
[Fact]
public async Task Post_DeleteMessageHandler_ReturnsRedirectToRoot()
{
// Arrange
using (var scope = _factory.Services.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<ApplicationDbContext>();
Utilities.ReinitializeDbForTests(db);
}
var defaultPage = await _client.GetAsync("/");
var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
//Act
var response = await _client.SendAsync(
(IHtmlFormElement)content.QuerySelector("form[id='messages']"),
(IHtmlButtonElement)content.QuerySelector("form[id='messages']")
.QuerySelector("div[class='panel-body']")
.QuerySelector("button"));
// Assert
Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode);
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.Equal("/", response.Headers.Location.OriginalString);
}
[TestMethod]
public async Task Post_DeleteMessageHandler_ReturnsRedirectToRoot()
{
// Arrange
using (var scope = _factory.Services.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<ApplicationDbContext>();
Utilities.ReinitializeDbForTests(db);
}
var defaultPage = await _client.GetAsync("/");
var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
//Act
var response = await _client.SendAsync(
(IHtmlFormElement)content.QuerySelector("form[id='messages']"),
(IHtmlButtonElement)content.QuerySelector("form[id='messages']")
.QuerySelector("div[class='panel-body']")
.QuerySelector("button"));
// Assert
Assert.AreEqual(HttpStatusCode.OK, defaultPage.StatusCode);
Assert.AreEqual(HttpStatusCode.Redirect, response.StatusCode);
Assert.AreEqual("/", response.Headers.Location.OriginalString);
}
[Test]
public async Task Post_DeleteMessageHandler_ReturnsRedirectToRoot()
{
// Arrange
using (var scope = _factory.Services.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<ApplicationDbContext>();
Utilities.ReinitializeDbForTests(db);
}
var defaultPage = await _client.GetAsync("/");
var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
//Act
var response = await _client.SendAsync(
(IHtmlFormElement)content.QuerySelector("form[id='messages']"),
(IHtmlButtonElement)content.QuerySelector("form[id='messages']")
.QuerySelector("div[class='panel-body']")
.QuerySelector("button"));
// Assert
Assert.That(defaultPage.StatusCode, Is.EqualTo(HttpStatusCode.OK));
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Redirect));
Assert.That(response.Headers.Location.OriginalString, Is.EqualTo("/"));
}
Ügyfélbeállítások
A WebApplicationFactoryClientOptions példányok létrehozásakor az alapértelmezett beállításokat és az elérhető beállításokat a HttpClient
lapon tekinti meg.
Hozza létre a WebApplicationFactoryClientOptions
osztályt, és adja át a CreateClient() metódusnak:
public class IndexPageTests :
IClassFixture<CustomWebApplicationFactory<Program>>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory<Program>
_factory;
public IndexPageTests(
CustomWebApplicationFactory<Program> factory)
{
_factory = factory;
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
}
[TestClass]
public class IndexPageTests
{
private static HttpClient _client;
private static CustomWebApplicationFactory<Program>
_factory;
[ClassInitialize]
public static void AssemblyInitialize(TestContext _)
{
_factory = new CustomWebApplicationFactory<Program>();
_client = _factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
}
[ClassCleanup(ClassCleanupBehavior.EndOfClass)]
public static void AssemblyCleanup(TestContext _)
{
_factory.Dispose();
}
public class IndexPageTests
{
private HttpClient _client;
private CustomWebApplicationFactory<Program>
_factory;
[SetUp]
public void SetUp()
{
_factory = new CustomWebApplicationFactory<Program>();
_client = _factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
}
[TearDown]
public void TearDown()
{
_factory.Dispose();
_client.Dispose();
}
MEGJEGYZÉS: A HTTPS-átirányítási figyelmeztetések elkerülése érdekében, amikor a HTTPS-átirányító köztes réteget használja, állítsa be a BaseAddress = new Uri("https://localhost")
Makettszolgáltatások injektálása
A szolgáltatások felülírhatók egy tesztben a ConfigureTestServices hívásával a gazdagép építőn. A felülbírált szolgáltatások konkrét teszt hatóköréhez a WithWebHostBuilder metódus használható egy hostkészítő lekérésére. Ez a következő tesztekben látható:
- Get_QuoteService_ProvidesQuoteInPage
- Szerezd_MegGithubProfilOldalAmiElérhetEgyGithubFelhasználót
- Biztonságos_ElőoldalVisszaadvaHitelesítettFelhasználónak
A minta SUT tartalmaz egy hatókörű szolgáltatást, amely egy idézetet ad vissza. Az indexlap kérésekor az idézet az Index lap rejtett mezőjébe van beágyazva.
Services/IQuoteService.cs
:
public interface IQuoteService
{
Task<string> GenerateQuote();
}
Services/QuoteService.cs
:
// Quote ©1975 BBC: The Doctor (Tom Baker); Dr. Who: Planet of Evil
// https://www.bbc.co.uk/programmes/p00pyrx6
public class QuoteService : IQuoteService
{
public Task<string> GenerateQuote()
{
return Task.FromResult<string>(
"Come on, Sarah. We've an appointment in London, " +
"and we're already 30,000 years late.");
}
}
Program.cs
:
services.AddScoped<IQuoteService, QuoteService>();
Pages/Index.cshtml.cs
:
public class IndexModel : PageModel
{
private readonly ApplicationDbContext _db;
private readonly IQuoteService _quoteService;
public IndexModel(ApplicationDbContext db, IQuoteService quoteService)
{
_db = db;
_quoteService = quoteService;
}
[BindProperty]
public Message Message { get; set; }
public IList<Message> Messages { get; private set; }
[TempData]
public string MessageAnalysisResult { get; set; }
public string Quote { get; private set; }
public async Task OnGetAsync()
{
Messages = await _db.GetMessagesAsync();
Quote = await _quoteService.GenerateQuote();
}
Pages/Index.cs
:
<input id="quote" type="hidden" value="@Model.Quote">
A SUT-alkalmazás futtatásakor a következő korrektúra jön létre:
<input id="quote" type="hidden" value="Come on, Sarah. We've an appointment in
London, and we're already 30,000 years late.">
A szolgáltatás és az injektálás teszteléséhez integrációs tesztben a teszt egy ál-szolgáltatást injektál a SUT-ba. A modellszolgáltatás lecseréli az alkalmazás QuoteService
-t a tesztalkalmazás által biztosított, TestQuoteService
nevű szolgáltatásra.
IntegrationTests.IndexPageTests.cs
:
// Quote ©1975 BBC: The Doctor (Tom Baker); Pyramids of Mars
// https://www.bbc.co.uk/programmes/p00pys55
public class TestQuoteService : IQuoteService
{
public Task<string> GenerateQuote()
{
return Task.FromResult(
"Something's interfering with time, Mr. Scarman, " +
"and time is my business.");
}
}
// Quote ©1975 BBC: The Doctor (Tom Baker); Pyramids of Mars
// https://www.bbc.co.uk/programmes/p00pys55
public class TestQuoteService : IQuoteService
{
public Task<string> GenerateQuote()
{
return Task.FromResult(
"Something's interfering with time, Mr. Scarman, " +
"and time is my business.");
}
}
// Quote ©1975 BBC: The Doctor (Tom Baker); Pyramids of Mars
// https://www.bbc.co.uk/programmes/p00pys55
public class TestQuoteService : IQuoteService
{
public Task<string> GenerateQuote()
{
return Task.FromResult(
"Something's interfering with time, Mr. Scarman, " +
"and time is my business.");
}
}
ConfigureTestServices
van meghívva, és a hatókörön belüli szolgáltatás regisztrálva van:
[Fact]
public async Task Get_QuoteService_ProvidesQuoteInPage()
{
// Arrange
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddScoped<IQuoteService, TestQuoteService>();
});
})
.CreateClient();
//Act
var defaultPage = await client.GetAsync("/");
var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
var quoteElement = content.QuerySelector("#quote");
// Assert
Assert.Equal("Something's interfering with time, Mr. Scarman, " +
"and time is my business.", quoteElement.Attributes["value"].Value);
}
[TestMethod]
public async Task Get_QuoteService_ProvidesQuoteInPage()
{
// Arrange
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddScoped<IQuoteService, TestQuoteService>();
});
})
.CreateClient();
//Act
var defaultPage = await client.GetAsync("/");
var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
var quoteElement = content.QuerySelector("#quote");
// Assert
Assert.AreEqual("Something's interfering with time, Mr. Scarman, " +
"and time is my business.", quoteElement.Attributes["value"].Value);
}
[Test]
public async Task Get_QuoteService_ProvidesQuoteInPage()
{
// Arrange
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddScoped<IQuoteService, TestQuoteService>();
});
})
.CreateClient();
//Act
var defaultPage = await client.GetAsync("/");
var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
var quoteElement = content.QuerySelector("#quote");
// Assert
Assert.That(quoteElement.Attributes["value"].Value, Is.EqualTo(
"Something's interfering with time, Mr. Scarman, " +
"and time is my business."));
}
A teszt végrehajtása során létrehozott jelölések a TestQuoteService
által megadott idézőjelszöveget tükrözik, ezért az állítás sikeres.
<input id="quote" type="hidden" value="Something's interfering with time,
Mr. Scarman, and time is my business.">
Szimulált hitelesítés
Az AuthTests
osztály tesztjei ellenőrzik, hogy egy biztonságos végpont:
- Átirányít egy nem hitelesített felhasználót az alkalmazás bejelentkezési oldalára.
- Egy hitelesített felhasználó tartalmát adja vissza.
A SUT-ban a /SecurePage
lap a AuthorizePage konvenciót használja, hogy egy AuthorizeFilter-t alkalmazzon a lapra. További információkért lásd a Razor oldalak engedélyezési konvencióit.
services.AddRazorPages(options =>
{
options.Conventions.AuthorizePage("/SecurePage");
});
A Get_SecurePageRedirectsAnUnauthenticatedUser
tesztben egy WebApplicationFactoryClientOptions úgy van beállítva, hogy letiltsa az átirányításokat azáltal, hogy AllowAutoRedirect-t false
-ra állítja.
[Fact]
public async Task Get_SecurePageRedirectsAnUnauthenticatedUser()
{
// Arrange
var client = _factory.CreateClient(
new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
// Act
var response = await client.GetAsync("/SecurePage");
// Assert
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.StartsWith("http://localhost/Identity/Account/Login",
response.Headers.Location.OriginalString);
}
[TestMethod]
public async Task Get_SecurePageRedirectsAnUnauthenticatedUser()
{
// Arrange
var client = _factory.CreateClient(
new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
// Act
var response = await client.GetAsync("/SecurePage");
// Assert
Assert.AreEqual(HttpStatusCode.Redirect, response.StatusCode);
StringAssert.StartsWith(response.Headers.Location.OriginalString, "http://localhost/Identity/Account/Login");
}
[Test]
public async Task Get_SecurePageRedirectsAnUnauthenticatedUser()
{
// Arrange
var client = _factory.CreateClient(
new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
// Act
var response = await client.GetAsync("/SecurePage");
// Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Redirect));
Assert.That(response.Headers.Location.OriginalString, Does.StartWith("http://localhost/Identity/Account/Login"));
}
Az ügyfél átirányításának megakadályozásával a következő ellenőrzéseket hajthatja végre:
- A SUT által visszaadott állapotkód a várt HttpStatusCode.Redirect eredményen ellenőrizhető, nem pedig a bejelentkezési lapra való átirányítás utáni végleges állapotkóddal, amely HttpStatusCode.OK.
- A válaszfejlécek közül a
Location
fejléc értékét ellenőrzik annak megállapítására, hogyhttp://localhost/Identity/Account/Login
-el kezdődik-e, nem pedig a végleges bejelentkezési oldal válaszát, ahol aLocation
fejléc nem lenne jelen.
A tesztalkalmazás szimulálhat egy AuthenticationHandler<TOptions>ConfigureTestServices-ben a hitelesítés és az engedélyezés szempontjainak teszteléséhez. Egy minimális forgatókönyv AuthenticateResult.Success-t ad vissza.
public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger, UrlEncoder encoder)
: base(options, logger, encoder)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var claims = new[] { new Claim(ClaimTypes.Name, "Test user") };
var identity = new ClaimsIdentity(claims, "Test");
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, "TestScheme");
var result = AuthenticateResult.Success(ticket);
return Task.FromResult(result);
}
}
public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger, UrlEncoder encoder)
: base(options, logger, encoder)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var claims = new[] { new Claim(ClaimTypes.Name, "Test user") };
var identity = new ClaimsIdentity(claims, "Test");
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, "TestScheme");
var result = AuthenticateResult.Success(ticket);
return Task.FromResult(result);
}
}
public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger, UrlEncoder encoder)
: base(options, logger, encoder)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var claims = new[] { new Claim(ClaimTypes.Name, "Test user") };
var identity = new ClaimsIdentity(claims, "Test");
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, "TestScheme");
var result = AuthenticateResult.Success(ticket);
return Task.FromResult(result);
}
}
A TestAuthHandler
egy felhasználó hitelesítésére hívják meg, amikor a hitelesítési séma TestScheme
van beállítva, ahol a AddAuthentication
regisztrálva van a ConfigureTestServices
-ra. Fontos, hogy a TestScheme
séma megfeleljen az alkalmazás által várt sémának. Ellenkező esetben a hitelesítés nem működik.
[Fact]
public async Task Get_SecurePageIsReturnedForAnAuthenticatedUser()
{
// Arrange
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddAuthentication(defaultScheme: "TestScheme")
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
"TestScheme", options => { });
});
})
.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false,
});
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(scheme: "TestScheme");
//Act
var response = await client.GetAsync("/SecurePage");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
[TestMethod]
public async Task Get_SecurePageIsReturnedForAnAuthenticatedUser()
{
// Arrange
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddAuthentication(defaultScheme: "TestScheme")
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
"TestScheme", options => { });
});
})
.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false,
});
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(scheme: "TestScheme");
//Act
var response = await client.GetAsync("/SecurePage");
// Assert
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
[Test]
public async Task Get_SecurePageIsReturnedForAnAuthenticatedUser()
{
// Arrange
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddAuthentication(defaultScheme: "TestScheme")
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
"TestScheme", options => { });
});
})
.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false,
});
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(scheme: "TestScheme");
//Act
var response = await client.GetAsync("/SecurePage");
// Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
}
A WebApplicationFactoryClientOptions
kapcsolatos további információkért lásd a Ügyfélbeállítások című szakaszt.
A köztes szoftver hitelesítésének alapszintű tesztjei
A Köztes szoftver hitelesítésének alapszintű teszteléséhez tekintse meg ezt a GitHub-adattárat. A tesztforgatókönyvre jellemző tesztkiszolgálót tartalmaz.
A környezet beállítása
Állítsa be a környezetet az egyéni alkalmazás-előállítóban:
public class CustomWebApplicationFactory<TProgram>
: WebApplicationFactory<TProgram> where TProgram : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var dbContextDescriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(IDbContextOptionsConfiguration<ApplicationDbContext>));
services.Remove(dbContextDescriptor);
var dbConnectionDescriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbConnection));
services.Remove(dbConnectionDescriptor);
// Create open SqliteConnection so EF won't automatically close it.
services.AddSingleton<DbConnection>(container =>
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
return connection;
});
services.AddDbContext<ApplicationDbContext>((container, options) =>
{
var connection = container.GetRequiredService<DbConnection>();
options.UseSqlite(connection);
});
});
builder.UseEnvironment("Development");
}
}
public class CustomWebApplicationFactory<TProgram>
: WebApplicationFactory<TProgram> where TProgram : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var dbContextDescriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(IDbContextOptionsConfiguration<ApplicationDbContext>));
services.Remove(dbContextDescriptor);
var dbConnectionDescriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbConnection));
services.Remove(dbConnectionDescriptor);
// Create open SqliteConnection so EF won't automatically close it.
services.AddSingleton<DbConnection>(container =>
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
return connection;
});
services.AddDbContext<ApplicationDbContext>((container, options) =>
{
var connection = container.GetRequiredService<DbConnection>();
options.UseSqlite(connection);
});
});
builder.UseEnvironment("Development");
}
}
public class CustomWebApplicationFactory<TProgram>
: WebApplicationFactory<TProgram> where TProgram : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var dbContextDescriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(IDbContextOptionsConfiguration<ApplicationDbContext>));
services.Remove(dbContextDescriptor);
var dbConnectionDescriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbConnection));
services.Remove(dbConnectionDescriptor);
// Create open SqliteConnection so EF won't automatically close it.
services.AddSingleton<DbConnection>(container =>
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
return connection;
});
services.AddDbContext<ApplicationDbContext>((container, options) =>
{
var connection = container.GetRequiredService<DbConnection>();
options.UseSqlite(connection);
});
});
builder.UseEnvironment("Development");
}
}
Hogyan következtet a tesztinfrastruktúra az alkalmazás tartalomgyökerének elérési útjára?
A WebApplicationFactory
konstruktor meghatározza az alkalmazás tartalom gyökér elérési útját úgy, hogy a WebApplicationFactoryContentRootAttribute szerelvény TEntryPoint
kulccsal a System.Reflection.Assembly.FullName
-t keres az integrációs teszteket tartalmazó szerelvényen. Ha nem található a megfelelő kulccsal rendelkező attribútum, WebApplicationFactory
visszatér a megoldásfájl (.sln) kereséséhez, és hozzáadja a TEntryPoint
szerelvény nevét a megoldáskönyvtárhoz. Az alkalmazás gyökérkönyvtára (a tartalom gyökérútvonala) a nézetek és tartalomfájlok felderítésére szolgál.
Árnyékmásolás letiltása
Az árnyékmásolás miatt a tesztek a kimeneti könyvtártól eltérő könyvtárban futnak. Ha a tesztek a fájlok Assembly.Location
-hoz viszonyított betöltésére támaszkodnak, és problémákat tapasztalunk, előfordulhat, hogy le kell tiltani az árnyékmásolást.
Ha le szeretné tiltani az árnyékmásolást az xUnit használatakor, hozzon létre egy xunit.runner.json
fájlt a tesztprojekt könyvtárában a megfelelő konfigurációs beállítással:
{
"shadowCopy": false
}
Objektumok ártalmatlanítása
A IClassFixture
implementáció tesztjeinek végrehajtása után TestServer és HttpClient megsemmisülnek, amikor az xUnit megsemmisíti a WebApplicationFactory
-at. Ha a fejlesztő által példányosított objektumok ártalmatlanítást igényelnek, azokat a IClassFixture
implementációban kell megsemmisíteni. További információért lásd: Dispose metódus implementálása.
A TestClass
tesztjeinek végrehajtása után az MSTest a TestServer metódusban megsemmisíti a HttpClient-et, ekkor pedig a WebApplicationFactory
és ClassCleanup
is megsemmisülnek. Ha a fejlesztő által példányosított objektumok megsemmisítést igényelnek, a ClassCleanup
metódusban kell megsemmisíteni őket. További információért lásd: Dispose metódus implementálása.
A tesztosztály tesztjeinek végrehajtása után az NUnit megsemmisíti a TestServer a HttpClient metódusban. Ha a fejlesztő által példányosított objektumok megsemmisítést igényelnek, a TearDown
metódusban kell megsemmisíteni őket. További információért lásd: Dispose metódus implementálása.
Integrációs tesztek mintája
A mintaalkalmazás két alkalmazásból áll:
Alkalmazás | Projektmappa | Leírás |
---|---|---|
Üzenetküldő alkalmazás (a SUT) | src/RazorPagesProject |
Lehetővé teszi a felhasználó számára, hogy hozzáadjon, töröljön egyet, törölje az összeset, és elemezze az üzeneteket. |
Alkalmazás tesztelése | tests/RazorPagesProject.Tests |
A SUT integrációs tesztelésére szolgál. |
A tesztek egy IDE beépített tesztfunkcióival futtathatók, például Visual Studio. Ha Visual Studio Code vagy parancssort használ, hajtsa végre a következő parancsot egy parancssorban a tests/RazorPagesProject.Tests
könyvtárban:
dotnet test
Üzenetküldő alkalmazás (SUT) szervezete
A SUT egy Razor Pages üzenetrendszer, amely a következő jellemzőkkel rendelkezik:
- Az alkalmazás indexoldala (
Pages/Index.cshtml
ésPages/Index.cshtml.cs
) felhasználói felületi és lapmodellezési módszereket biztosít az üzenetek hozzáadásának, törlésének és elemzésének szabályozásához (átlagos szavak üzenetenként). - Az üzeneteket a
Message
osztály (Data/Message.cs
) írja le két tulajdonságokkal:Id
(kulcs) ésText
(üzenet). AText
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 egy adatelérési réteget (DAL) tartalmaz az adatbázis környezeti osztályában,
AppDbContext
(Data/AppDbContext.cs
). - Ha az adatbázis üres az alkalmazás indításakor, az üzenettároló három üzenettel lesz inicializálva.
- Az alkalmazás tartalmaz egy
/SecurePage
, amelyet csak hitelesített felhasználó érhet el.
†Az EF-cikk, Az InMemorytesztelése című cikk 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 az alkalmazás nem használja az adattár mintát, és nem hatékony példa a Munkaegység (UoW) mintára, a Razor Pages támogatja ezeket a fejlesztési mintákat. További információ: Az infrastruktúra perzisztenciaréteg tervezése és Tesztvezérlő logika (a minta implementálja az adattár mintát).
Tesztalkalmazás szervezése
A tesztalkalmazás egy konzolalkalmazás a tests/RazorPagesProject.Tests
könyvtárban.
Alkalmazáskönyvtár tesztelése | Leírás |
---|---|
AuthTests |
A következő tesztelési módszereket tartalmazza:
|
BasicTests |
Az útválasztás és a tartalomtípus tesztelési módszerét tartalmazza. |
IntegrationTests |
Az Index lap integrációs tesztjeit tartalmazza egyéni WebApplicationFactory osztály használatával. |
Helpers/Utilities |
|
A tesztelési keretrendszer xUnit. Az integrációs teszteket a Microsoft.AspNetCore.TestHostsegítségével hajtják végre, amely magában foglalja a TestServer-et. Mivel a Microsoft.AspNetCore.Mvc.Testing
csomag a tesztgazda és a tesztkiszolgáló konfigurálására szolgál, a TestHost
és TestServer
csomagok nem igényelnek közvetlen csomaghivatkozásokat a tesztalkalmazás projektfájljában vagy fejlesztői konfigurációjában a tesztalkalmazásban.
Az integrációs tesztek általában kis adatkészletet igényelnek az adatbázisban a teszt végrehajtása előtt. A törlési teszt például egy adatbázisrekord törlését kéri, ezért az adatbázisnak legalább egy rekorddal kell rendelkeznie ahhoz, hogy a törlési kérelem sikeres legyen.
A mintaalkalmazás három üzenettel tölti fel az adatbázist a Utilities.cs
-ben, amelyeket a tesztek futtatásakor használhatnak.
public static void InitializeDbForTests(ApplicationDbContext db)
{
db.Messages.AddRange(GetSeedingMessages());
db.SaveChanges();
}
public static void ReinitializeDbForTests(ApplicationDbContext db)
{
db.Messages.RemoveRange(db.Messages);
InitializeDbForTests(db);
}
public static List<Message> GetSeedingMessages()
{
return new List<Message>()
{
new Message(){ Text = "TEST RECORD: You're standing on my scarf." },
new Message(){ Text = "TEST RECORD: Would you like a jelly baby?" },
new Message(){ Text = "TEST RECORD: To the rational mind, " +
"nothing is inexplicable; only unexplained." }
};
}
A SUT adatbázis-környezetét regisztrálták Program.cs
-ben. A tesztalkalmazás builder.ConfigureServices
visszahívása az alkalmazás lesz végrehajtva. Ha másik adatbázist szeretne használni a tesztekhez, az alkalmazás adatbázis-környezetét le kell cserélni a builder.ConfigureServices
-ban. A további információkért lásd a WebApplicationFactory testreszabása szakaszt.
További erőforrások
- egységtesztek
- Razor Lapok egységtesztjei ASP.NET Core
- ASP.NET Core köztes szoftver
- Tesztvezérlő logika az ASP.NET Core-ban
- Köztes szoftver hitelesítésének alapszintű tesztjei