Testy integracji zapewniają prawidłowe działanie składników aplikacji na poziomie obejmującym infrastrukturę pomocniczą aplikacji, taką jak baza danych, system plików i sieć. ASP.NET Core obsługuje testy integracji przy użyciu struktury testów jednostkowych z testowym hostem internetowym i serwerem testowym w pamięci.
Do testowania spAs zalecamy narzędzie, takie jak Playwright dla platformy .NET, które może zautomatyzować przeglądarkę.
Wprowadzenie do testów integracji
Testy integracji oceniają składniki aplikacji na szerszym poziomie niż testy jednostkowe. Testy jednostkowe służą do testowania izolowanych składników oprogramowania, takich jak poszczególne metody klasy. Testy integracji potwierdzają, że co najmniej dwa składniki aplikacji współpracują ze sobą, aby wygenerować oczekiwany wynik, prawdopodobnie włącznie z każdym składnikiem wymaganym do pełnego przetworzenia żądania.
Te szersze testy służą do testowania infrastruktury aplikacji i całej struktury, często w tym następujących składników:
baza danych
System plików
Urządzenia sieciowe
Przepływ żądań i odpowiedzi
Testy jednostkowe używają sfabrykowanych składników, znanych jako fałszywe lub makiety obiektów, zamiast składników infrastruktury.
W przeciwieństwie do testów jednostkowych, testy integracji:
Użyj rzeczywistych składników używanych przez aplikację w środowisku produkcyjnym.
Wymagaj więcej kodu i przetwarzania danych.
Uruchamianie trwa dłużej.
W związku z tym ogranicz użycie testów integracji do najważniejszych scenariuszy infrastruktury. Jeśli zachowanie można przetestować przy użyciu testu jednostkowego lub testu integracji, wybierz test jednostkowy.
W dyskusjach na temat testów integracyjnych testowany projekt jest często nazywany Systemem Pod Testami, lub w skrócie "SUT". Element "SUT" jest używany w tym artykule do odwoływania się do testowanej aplikacji ASP.NET Core.
Nie zapisuj testów integracji dla każdej permutacji danych i dostępu do plików za pomocą baz danych i systemów plików. Niezależnie od liczby miejsc w aplikacji współdziała z bazami danych i systemami plików, skoncentrowany zestaw testów integracji odczytu, zapisu, aktualizacji i usuwania zwykle umożliwia odpowiednie testowanie składników bazy danych i systemu plików. Używaj testów jednostkowych do rutynowych testów logiki metod, które wchodzą w interakcje z tymi składnikami. W testach jednostkowych użycie fałszywych elementów infrastruktury lub imitacji przyspiesza wykonywanie testów.
testy integracji ASP.NET Core
Testy integracji w programie ASP.NET Core wymagają następujących elementów:
Projekt testowy służy do zawierania i wykonywania testów. Projekt testowy zawiera odwołanie do SUT.
Projekt testowy tworzy serwer testowy dla SUT i korzysta z klienta serwera testowego do obsługi żądań i odpowiedzi z SUT.
Moduł uruchamiający testy służy do wykonywania testów i zgłaszania wyników testu.
Testy integracji są zgodne z sekwencją zdarzeń obejmującą zwykłe kroki testowe: Rozmieszczenie, Działanie i Sprawdzenie.
Host internetowy SUT jest skonfigurowany.
Klient serwera testowego jest tworzony w celu przesyłania żądań do aplikacji.
Etap Aranżacji testu jest realizowany: aplikacja testowa przygotowuje żądanie.
Krok testu Act jest realizowany: klient wysyła żądanie i odbiera odpowiedź.
Krok testu potwierdzenia jest wykonywany: rzeczywista odpowiedź jest weryfikowana jako powodzenie lub niepowodzenie w oparciu o oczekiwaną odpowiedź.
Proces będzie kontynuowany do momentu wykonania wszystkich testów.
Wyniki testów są zgłaszane.
Zazwyczaj testowy host internetowy jest skonfigurowany inaczej niż normalny host internetowy aplikacji na potrzeby przebiegów testu. Na przykład do testów mogą być używane różne ustawienia bazy danych lub różnych aplikacji.
Składniki infrastruktury, takie jak testowy host internetowy i serwer testowy w pamięci (TestServer), są dostarczane lub zarządzane przez pakiet Microsoft.AspNetCore.Mvc.Testing . Użycie tego pakietu usprawnia tworzenie i wykonywanie testów.
Kopiuje plik zależności (.deps) z sutu do katalogu projektu testowego bin .
Ustawia katalog główny zawartości na katalog główny projektu SUT, tak aby pliki statyczne i strony/widoki zostały znalezione podczas wykonywania testów.
Udostępnia klasę WebApplicationFactory , aby usprawnić uruchamianie aplikacji SUT za pomocą polecenia TestServer.
W dokumentacji testów jednostkowych opisano sposób konfigurowania projektu testowego i modułu uruchamiającego testy oraz szczegółowe instrukcje dotyczące uruchamiania testów i zaleceń dotyczących sposobu nazywania testów i klas testowych.
Oddziel testy jednostkowe od testów integracji do różnych projektów. Oddzielanie testów:
Pomaga zagwarantować, że składniki testowania infrastruktury nie zostaną przypadkowo uwzględnione w testach jednostkowych.
Umożliwia kontrolę nad tym, który zestaw testów jest uruchamiany.
Praktycznie nie ma różnicy między konfiguracją testów Razor aplikacji Pages i aplikacji MVC. Jedyną różnicą jest sposób, w jaki testy są nazwane.
Razor W aplikacji Pages testy punktów końcowych strony są zwykle nazwane po klasie modelu strony (na przykład IndexPageTests w celu przetestowania integracji składników dla strony Indeks). W aplikacji MVC testy są zwykle zorganizowane według klas kontrolerów i nazwane po testowych kontrolerach (na przykład HomeControllerTests do testowania integracji składników dla Home kontrolera).
Określ zestaw Web SDK w pliku projektu (<Project Sdk="Microsoft.NET.Sdk.Web">).
Te wymagania wstępne można zobaczyć w przykładowej aplikacji.
tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj Sprawdź plik. Przykładowa aplikacja używa struktury testowej xUnit i biblioteki analizatora AngleSharp , więc przykładowa aplikacja również odwołuje się do:
Klasy testowe implementują interfejs fixture klasy (IClassFixture), aby wskazać, że klasa zawiera testy i udostępnia wspólne wystąpienia obiektów między testami w klasie.
Następująca klasa testowa BasicTests używa WebApplicationFactory do uruchomienia SUT i dostarczenia HttpClient do metody testowej Get_EndpointsReturnSuccessAndCorrectContentType. Metoda sprawdza, czy kod stanu odpowiedzi jest pomyślny (200–299), a nagłówek ma wartość Content-Type na kilku stronach aplikacji.
CreateClient() Program tworzy wystąpienie HttpClient , które automatycznie śledzi przekierowania i obsługuje pliki cookie.
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());
}
}
Domyślnie pliki cookie inne niż podstawowe nie są zachowywane w żądaniach, gdy zasady zgody ogólnego rozporządzenia o ochronie danych są włączone. Aby zachować nieistotne pliki cookie, takie jak te używane przez dostawcę TempData, oznacz je jako niezbędne w testach. Aby uzyskać instrukcje dotyczące oznaczania cookie elementu jako niezbędnego, zobacz Podstawowe pliki cookie.
AngleSharp vs Application Parts kontra antyfałszerstwo
W tym artykule użyto analizatora AngleSharp do obsługi testów antyforgeryjnych przez ładowanie stron i analizowanie kodu HTML. Aby przetestować punkty końcowe kontrolera i widoki stron Razor na niższym poziomie, bez martwienia się o sposób renderowania w przeglądarce, rozważ użycie Application Parts. Podejście Części Aplikacji wprowadza kontroler lub Razor Stronę do aplikacji, którego można użyć do składania żądań JSON w celu uzyskania wymaganych wartości. Aby uzyskać więcej informacji, zobacz blog Testowanie integracji zasobów ASP.NET Core chronionych przed fałszerstwami przy użyciu części aplikacji oraz powiązane repozytorium GitHub autorstwa Martina Costello.
Dostosować WebApplicationFactory
Konfigurację hosta internetowego można utworzyć niezależnie od klas testowych, dziedzicząc z WebApplicationFactory<TEntryPoint> w celu utworzenia co najmniej jednej fabryki niestandardowej:
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");
}
}
Kontekst bazy danych SUT jest zarejestrowany w pliku Program.cs. Wywołanie zwrotne aplikacji builder.ConfigureServices testowej jest wykonywane po wykonaniu kodu aplikacji Program.cs . Aby użyć innej bazy danych do testów niż baza danych aplikacji, kontekst bazy danych aplikacji musi zostać zastąpiony w pliku builder.ConfigureServices.
Przykładowa aplikacja znajduje deskryptor usługi dla kontekstu bazy danych i używa deskryptora do usunięcia rejestracji usługi. Następnie fabryka dodaje nowy element ApplicationDbContext, który do testów używa bazy danych w pamięci.
Aby nawiązać połączenie z inną bazą danych, zmień wartość DbConnection. Aby użyć testowej bazy danych programu SQL Server:
Dodaj odwołanie do pakietu NuGet w pliku projektu.
Zadzwoń UseInMemoryDatabase.
Użyj niestandardowego CustomWebApplicationFactory w klasach testowych. W poniższym przykładzie użyto fabryki w klasie IndexPageTests.
Klient przykładowej aplikacji jest skonfigurowany tak, aby zapobiec HttpClient następującym przekierowaniom. Jak wyjaśniono w sekcji Mock uwierzytelnianie, pozwala to testom na sprawdzenie wyniku pierwszej odpowiedzi aplikacji. Pierwsza odpowiedź w wielu z tych testów to przekierowanie z nagłówkiem Location.
Typowy test używa HttpClient oraz metod pomocniczych do przetwarzania żądania i odpowiedzi.
[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);
}
Każde żądanie POST do SUT musi spełniać kontrolę antyforgeryjną, która jest automatycznie dokonana przez system ochrony danych aplikacji. Aby zorganizować żądanie POST testu, aplikacja testowa musi:
Utwórz żądanie dla strony.
Przeanalizuj token antyforgery i cookie żądaj weryfikacji z odpowiedzi.
Utwórz żądanie POST z tokenem antyforgery i cookie zażądaj weryfikacji.
SendAsync Pomocnicze metody rozszerzenia (Helpers/HttpClientExtensions.cs) i metoda pomocnika (GetDocumentAsync) w przykładowej Helpers/HtmlHelpers.cs używają analizatora AngleSharp do obsługi sprawdzania pod kątem ochrony przed fałszerstwami za pomocą następujących metod:
GetDocumentAsync: Odbiera element HttpResponseMessage i zwraca wartość IHtmlDocument.
GetDocumentAsync używa mechanizmu, który przygotowuje wirtualną odpowiedź na podstawie oryginalnego HttpResponseMessage. Aby uzyskać więcej informacji, zobacz dokumentację AngleSharp.
SendAsync metody rozszerzenia dla HttpClient tworzą HttpRequestMessage i wywołują SendAsync(HttpRequestMessage) w celu przesłania żądań do SUT. Przeciążenia dla SendAsync akceptują formularz HTML (IHtmlFormElement) i następujące elementy:
Przycisk Prześlij formularza (IHtmlElement)
Kolekcja wartości formularza (IEnumerable<KeyValuePair<string, string>>)
Przycisk Prześlij (IHtmlElement) i wartości formularza (IEnumerable<KeyValuePair<string, string>>)
AngleSharp to biblioteka do analizy od strony trzeciej używana do celów demonstracyjnych w tym artykule i przykładowej aplikacji. Funkcja AngleSharp nie jest obsługiwana ani wymagana do testowania integracji aplikacji ASP.NET Core. Można użyć innych analizatorów, takich jak Pakiet Zwinności HTML (HAP). Innym podejściem jest napisanie kodu do obsługi tokenu weryfikacji żądania systemu ochrony przed fałszerstwem oraz bezpośredniego zarządzania systemem antyfałszerstwowym cookie. Aby uzyskać więcej informacji, zobacz AngleSharp kontra Application Parts do weryfikacji antyfałszerstw w tym artykule.
Dostawca bazy danych EF-Core w pamięci może służyć do ograniczonego i podstawowego testowania, jednak dostawca SQLite jest zalecanym wyborem do testowania w pamięci.
Dostosowywanie klienta za pomocą programu WithWebHostBuilder
Jeśli w metodzie testowej wymagana jest dodatkowa konfiguracja, WithWebHostBuilder tworzy nowy WebApplicationFactory, zawierający IWebHostBuilder, który został dalej skonfigurowany zgodnie z potrzebami.
Przykładowy kod wywołuje aby zastąpić skonfigurowane usługi próbkami testowymi. Aby uzyskać więcej informacji i przykłady użycia, zapoznaj się z sekcją Wstrzykiwanie usług mock w tym artykule.
Metoda Post_DeleteMessageHandler_ReturnsRedirectToRoot testowa przykładowej aplikacji demonstruje użycie metody WithWebHostBuilder. Ten test wykonuje usunięcie rekordu w bazie danych, wyzwalając przesłanie formularza w SUT.
Ponieważ inny test w IndexPageTests klasie wykonuje operację, która usuwa wszystkie rekordy w bazie danych i może zostać uruchomiona przed Post_DeleteMessageHandler_ReturnsRedirectToRoot metodą, baza danych jest ponownie zainicjowana w tej metodzie testowej, aby upewnić się, że rekord jest obecny, aby SUT mógł usunąć. Wybranie pierwszego przycisku usuwania w formularzu messages w SUT jest symulowane w żądaniu do SUT:
[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);
}
Opcje klienta
Zobacz stronę WebApplicationFactoryClientOptions dotyczącą ustawień domyślnych i dostępnych opcji podczas tworzenia instancji HttpClient.
Utwórz klasę WebApplicationFactoryClientOptions i przekaż ją do CreateClient() metody :
UWAGA: Aby uniknąć ostrzeżeń o przekierowaniach HTTPS w dziennikach podczas korzystania z HTTPS Redirection Middleware, ustaw BaseAddress = new Uri("https://localhost")
Wstrzyknij usługi testowe
Usługi można zastąpić podczas testu za pomocą wywołania metody ConfigureTestServices w konstruktorze hosta. Aby ograniczyć zakres nadpisanych usług do samego testu, metoda WithWebHostBuilder jest używana w celu uzyskania konstruktora hosta. Można to zobaczyć w następujących testach:
Przykładowy SUT zawiera usługę o zdefiniowanym zakresie, która zwraca cytat. Cytat jest osadzony w ukrytym polu na stronie indeks, gdy żądana jest strona indeks.
Services/IQuoteService.cs:
public interface IQuoteService
{
Task<string> GenerateQuote();
}
Po uruchomieniu aplikacji SUT jest generowany następujący kod:
<input id="quote" type="hidden" value="Come on, Sarah. We've an appointment in
London, and we're already 30,000 years late.">
Aby przetestować usługę i przeprowadzić iniekcję w teście integracji, usługa symulowana jest wstrzykiwana do systemu testowanego przez test. Usługa pozorowania zastępuje aplikację QuoteService usługą dostarczaną przez aplikację testową o nazwie TestQuoteService:
ConfigureTestServices zostaje wywołana, a usługa o określonym zakresie zostaje zarejestrowana:
[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);
}
Znacznik wygenerowany podczas wykonywania testu odzwierciedla tekst cudzysłowu dostarczony przez element TestQuoteService, w związku z czym asercja przechodzi.
<input id="quote" type="hidden" value="Something's interfering with time,
Mr. Scarman, and time is my business.">
Symulacja uwierzytelniania
Testy w AuthTests klasie sprawdzają, czy bezpieczny punkt końcowy:
Przekierowuje nieuwierzytelnionego użytkownika do strony logowania aplikacji.
Zwraca zawartość dla uwierzytelnionego użytkownika.
Na stronie w SUT /SecurePage stosuje się konwencję AuthorizePage do zastosowania elementu AuthorizeFilter na stronie. Aby uzyskać więcej informacji, zobacz Razor Konwencje autoryzacji stron.
[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);
}
Nie zezwalając klientowi na obserwowanie przekierowania, można wykonać następujące kontrole:
Kod stanu zwrócony przez SUT można sprawdzić względem oczekiwanego HttpStatusCode.Redirect wyniku, a nie końcowego kodu stanu po przekierowaniu do strony logowania, który byłby HttpStatusCode.OK.
Wartość nagłówka Location w nagłówkach odpowiedzi jest sprawdzana, aby potwierdzić, że rozpoczyna się od http://localhost/Identity/Account/Login, a nie od odpowiedzi końcowej strony logowania, gdzie nagłówek Location nie byłby obecny.
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);
}
}
Element TestAuthHandler jest wywoływany w celu uwierzytelnienia użytkownika, gdy ustawiono schemat uwierzytelniania jako TestScheme, gdzie AddAuthentication jest zarejestrowany dla ConfigureTestServices. Ważne jest, TestScheme aby schemat był zgodny ze schematem oczekiwanym przez aplikację. W przeciwnym razie uwierzytelnianie nie będzie działać.
Zobacz to repozytorium GitHub, aby zapoznać się z podstawowymi testami oprogramowania pośredniczącego uwierzytelniania. Zawiera on serwer testowy specyficzny dla scenariusza testowego.
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");
}
}
Jak infrastruktura testowa wywnioskuje ścieżkę główną zawartości aplikacji
Konstruktor WebApplicationFactory ustala ścieżkę katalogu głównego zawartości aplikacji poprzez wyszukiwanie elementu WebApplicationFactoryContentRootAttribute w zestawie, który zawiera testy integracyjne, z kluczem równym zestawowi TEntryPointSystem.Reflection.Assembly.FullName. Jeśli atrybut z poprawnym kluczem nie zostanie znaleziony, WebApplicationFactory wraca do wyszukiwania pliku rozwiązania (.sln) i dodaje TEntryPoint nazwę zestawu do katalogu rozwiązania. Katalog główny aplikacji (ścieżka główna zawartości) służy do odnajdywania widoków i plików zawartości.
Wyłącz kopiowanie w tle
Kopiowanie w tle powoduje wykonanie testów w innym katalogu niż katalog wyjściowy. Jeśli testy polegają na ładowaniu plików względem Assembly.Location i napotkasz problemy, może być konieczne wyłączenie kopiowania w tle.
Aby wyłączyć kopiowanie cieni przy użyciu xUnit, utwórz plik xunit.runner.json w katalogu projektu testowego z poprawnym ustawieniem konfiguracji:
{
"shadowCopy": false
}
Usuwanie obiektów
Po przeprowadzeniu testów implementacji IClassFixture, TestServer i HttpClient są usuwane, gdy xUnit usuwa WebApplicationFactory. Jeśli obiekty utworzone przez dewelopera wymagają usunięcia, należy je usunąć w implementacji IClassFixture . Aby uzyskać więcej informacji, zobacz Metoda Dispose — implementacja.
Przykład testów integracji
Przykładowa aplikacja składa się z dwóch aplikacji:
Aplikacja
Katalog projektu
opis
Aplikacja komunikatów (SUT)
src/RazorPagesProject
Umożliwia użytkownikowi dodawanie, usuwanie jednego, usuwanie wszystkich i analizowanie komunikatów.
Testowanie aplikacji
tests/RazorPagesProject.Tests
Służy do testowania integracji systemu (SUT).
Testy można uruchamiać przy użyciu wbudowanych funkcji testowych środowiska IDE, takich jak Visual Studio. Jeśli używasz programu Visual Studio Code lub wiersza polecenia, wykonaj następujące polecenie w wierszu polecenia w tests/RazorPagesProject.Tests katalogu:
dotnet test
Organizacja aplikacji wiadomości (SUT)
SUT to Razor system komunikatów Pages o następujących cechach:
Strona Indeks aplikacji (Pages/Index.cshtml i Pages/Index.cshtml.cs) udostępnia metody interfejsu użytkownika i modelu strony do kontrolowania dodawania, usuwania i analizowania komunikatów (średnie słowa na komunikat).
Komunikat jest opisany przez klasę Message (Data/Message.cs) z dwiema właściwościami: Id (klucz) i Text (komunikat). Właściwość Text jest wymagana i ograniczona do 200 znaków.
Komunikaty są przechowywane przy użyciu bazy danych w pamięci programu Entity Framework†.
Aplikacja zawiera warstwę dostępu do danych (DAL) w swojej klasie AppDbContext kontekstu bazy danych (Data/AppDbContext.cs).
Jeśli baza danych jest pusta podczas uruchamiania aplikacji, magazyn komunikatów jest inicjowany z trzema komunikatami.
Aplikacja zawiera element /SecurePage , do którego można uzyskać dostęp tylko przez uwierzytelnionego użytkownika.
† Artykuł EF, Test z InMemory, wyjaśnia, jak używać bazy danych w pamięci do testów przy użyciu narzędzia MSTest. W tym temacie jest używana struktura testowa xUnit . Koncepcje testów i implementacje testów w różnych platformach testowych są podobne, ale nie identyczne.
Aplikacja testowa jest aplikacją konsolową wewnątrz tests/RazorPagesProject.Tests katalogu.
Testowanie katalogu aplikacji
opis
AuthTests
Zawiera metody testowe dla:
Uzyskiwanie dostępu do bezpiecznej strony przez nieuwierzytelnionego użytkownika.
Uzyskiwanie dostępu do bezpiecznej strony przez uwierzytelnionego użytkownika z użyciem pozornego elementu AuthenticationHandler<TOptions>.
Uzyskiwanie profilu użytkownika usługi GitHub i sprawdzanie logowania użytkownika profilu.
BasicTests
Zawiera metodę testową routingu i typu zawartości.
IntegrationTests
Zawiera testy integracji dla strony Indeks przy użyciu klasy niestandardowej WebApplicationFactory .
Helpers/Utilities
Utilities.cs zawiera metodę InitializeDbForTests, używaną do zasiewania bazy danych danymi testowymi.
HtmlHelpers.cs Udostępnia metodę zwracania obiektu AngleSharp IHtmlDocument do użytku przez metody testowe.
HttpClientExtensions.cs zapewnij przeciążenia dla SendAsync aby przesyłać żądania do SUT.
Framework testowy to xUnit. Testy integracji są przeprowadzane przy użyciu Microsoft.AspNetCore.TestHost, który zawiera TestServer. Ponieważ pakiet Microsoft.AspNetCore.Mvc.Testing jest używany do konfigurowania hosta testowego i serwera testowego, pakiety TestHost i TestServer nie wymagają bezpośrednich odwołań w pliku projektu aplikacji testowej ani ręcznej konfiguracji przez dewelopera w tej aplikacji.
Testy integracji zwykle wymagają małego zestawu danych w bazie danych przed wykonaniem testu. Na przykład test usuwania wywołuje usunięcie rekordu bazy danych, więc baza danych musi mieć co najmniej jeden rekord, aby żądanie usunięcia powiodło się.
Przykładowa aplikacja zapełnia bazę danych trzema komunikatami w Utilities.cs, które mogą być używane przez testy podczas ich wykonywania:
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." }
};
}
Kontekst bazy danych SUT jest zarejestrowany w pliku Program.cs. Wywołanie zwrotne aplikacji builder.ConfigureServices testowej jest wykonywane po wykonaniu kodu aplikacji Program.cs . Aby użyć innej bazy danych do testów, kontekst bazy danych aplikacji musi zostać zastąpiony w pliku builder.ConfigureServices. Aby uzyskać więcej informacji, zobacz sekcję Customize WebApplicationFactory.
Zakłada się, że czytelnicy mają podstawową wiedzę na temat testów jednostkowych. Jeśli nie masz pewności co do pojęć związanych z testowaniem, zobacz temat Unit Testing in .NET Core and .NET Standard (Testowanie jednostkowe na platformie .NET Core i .NET Standard ) oraz jego połączoną zawartość.
Przykładowa aplikacja to Razor Pages app i wymaga podstawowej znajomości Razor Pages. Jeśli nie jesteś zaznajomiony z Razor Pages, zapoznaj się z następującymi tematami:
Do testowania SPA zalecamy narzędzie, takie jak Playwright for .NET, które może zautomatyzować przeglądarkę.
Wprowadzenie do testów integracji
Testy integracji oceniają składniki aplikacji na szerszym poziomie niż testy jednostkowe. Testy jednostkowe służą do testowania izolowanych składników oprogramowania, takich jak poszczególne metody klasy. Testy integracji potwierdzają, że co najmniej dwa składniki aplikacji współpracują ze sobą, aby wygenerować oczekiwany wynik, prawdopodobnie włącznie z każdym składnikiem wymaganym do pełnego przetworzenia żądania.
Te szersze testy służą do testowania infrastruktury aplikacji i całej struktury, często w tym następujących składników:
baza danych
System plików
Urządzenia sieciowe
Przepływ żądań i odpowiedzi
Testy jednostkowe używają sfabrykowanych składników, znanych jako fałszywe lub makiety obiektów, zamiast składników infrastruktury.
W przeciwieństwie do testów jednostkowych, testy integracji:
Użyj rzeczywistych składników używanych przez aplikację w środowisku produkcyjnym.
Wymagaj więcej kodu i przetwarzania danych.
Uruchamianie trwa dłużej.
W związku z tym ogranicz użycie testów integracji do najważniejszych scenariuszy infrastruktury. Jeśli zachowanie można przetestować przy użyciu testu jednostkowego lub testu integracji, wybierz test jednostkowy.
W dyskusjach na temat testów integracyjnych testowany projekt jest często nazywany Systemem Pod Testami, lub w skrócie "SUT". Element "SUT" jest używany w tym artykule do odwoływania się do testowanej aplikacji ASP.NET Core.
Nie zapisuj testów integracji dla każdej permutacji danych i dostępu do plików za pomocą baz danych i systemów plików. Niezależnie od liczby miejsc w aplikacji współdziała z bazami danych i systemami plików, skoncentrowany zestaw testów integracji odczytu, zapisu, aktualizacji i usuwania zwykle umożliwia odpowiednie testowanie składników bazy danych i systemu plików. Używaj testów jednostkowych do rutynowych testów logiki metod, które wchodzą w interakcje z tymi składnikami. W testach jednostkowych użycie fałszywych elementów infrastruktury lub imitacji przyspiesza wykonywanie testów.
testy integracji ASP.NET Core
Testy integracji w programie ASP.NET Core wymagają następujących elementów:
Projekt testowy służy do zawierania i wykonywania testów. Projekt testowy zawiera odwołanie do SUT.
Projekt testowy tworzy serwer testowy dla SUT i korzysta z klienta serwera testowego do obsługi żądań i odpowiedzi z SUT.
Moduł uruchamiający testy służy do wykonywania testów i zgłaszania wyników testu.
Testy integracji są zgodne z sekwencją zdarzeń obejmującą zwykłe kroki testowe: Rozmieszczenie, Działanie i Sprawdzenie.
Host internetowy SUT jest skonfigurowany.
Klient serwera testowego jest tworzony w celu przesyłania żądań do aplikacji.
Etap Aranżacji testu jest realizowany: aplikacja testowa przygotowuje żądanie.
Krok testu Akcji jest wykonywany: klient przesyła żądanie i odbiera odpowiedź.
Krok testu potwierdzenia jest wykonywany: rzeczywista odpowiedź jest weryfikowana jako powodzenie lub niepowodzenie w oparciu o oczekiwaną odpowiedź.
Proces będzie kontynuowany do momentu wykonania wszystkich testów.
Wyniki testów zostały przedstawione.
Zazwyczaj testowy host internetowy jest skonfigurowany inaczej niż normalny host internetowy aplikacji na potrzeby przebiegów testu. Na przykład do testów mogą być używane różne ustawienia bazy danych lub różnych aplikacji.
Składniki infrastruktury, takie jak testowy host internetowy i serwer testowy w pamięci (TestServer), są dostarczane lub zarządzane przez pakiet Microsoft.AspNetCore.Mvc.Testing . Użycie tego pakietu usprawnia tworzenie i wykonywanie testów.
Kopiuje plik zależności (.deps) z sutu do katalogu projektu testowego bin .
Ustawia katalog główny zawartości na katalog główny projektu SUT, tak aby pliki statyczne i strony/widoki zostały znalezione podczas wykonywania testów.
Udostępnia klasę WebApplicationFactory , aby usprawnić uruchamianie aplikacji SUT za pomocą polecenia TestServer.
W dokumentacji testów jednostkowych opisano sposób konfigurowania projektu testowego i modułu uruchamiającego testy oraz szczegółowe instrukcje dotyczące uruchamiania testów i zaleceń dotyczących sposobu nazywania testów i klas testowych.
Oddziel testy jednostkowe od testów integracji do różnych projektów. Oddzielanie testów:
Pomaga zagwarantować, że składniki testowania infrastruktury nie zostaną przypadkowo uwzględnione w testach jednostkowych.
Umożliwia kontrolę nad tym, który zestaw testów jest uruchamiany.
Praktycznie nie ma różnicy między konfiguracją testów Razor aplikacji Pages i aplikacji MVC. Jedyną różnicą jest sposób, w jaki testy są nazwane.
Razor W aplikacji Pages testy punktów końcowych strony są zwykle nazwane po klasie modelu strony (na przykład IndexPageTests w celu przetestowania integracji składników dla strony Indeks). W aplikacji MVC testy są zwykle zorganizowane według klas kontrolerów i nazwane po testowych kontrolerach (na przykład HomeControllerTests do testowania integracji składników dla Home kontrolera).
Określ zestaw Web SDK w pliku projektu (<Project Sdk="Microsoft.NET.Sdk.Web">).
Te wymagania wstępne można zobaczyć w przykładowej aplikacji.
tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj Sprawdź plik. Przykładowa aplikacja używa struktury testowej xUnit i biblioteki analizatora AngleSharp , więc przykładowa aplikacja również odwołuje się do:
Klasy testowe implementują interfejs fixture klasy (IClassFixture), aby wskazać, że klasa zawiera testy i udostępnia wspólne wystąpienia obiektów między testami w klasie.
Następująca klasa testowa BasicTests używa WebApplicationFactory do uruchomienia SUT i dostarczenia HttpClient do metody testowej Get_EndpointsReturnSuccessAndCorrectContentType. Metoda sprawdza, czy kod stanu odpowiedzi jest pomyślny (kody stanu w zakresie od 200 do 299) oraz czy Content-Type nagłówek jest text/html; charset=utf-8 dla kilku stron aplikacji.
CreateClient() Program tworzy wystąpienie HttpClient , które automatycznie śledzi przekierowania i obsługuje pliki cookie.
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());
}
}
Domyślnie pliki cookie inne niż podstawowe nie są zachowywane przy różnych żądaniach, gdy polityka zgody RODO jest włączona. Aby zachować nieistotne pliki cookie, takie jak te używane przez dostawcę TempData, oznacz je jako niezbędne w testach. Aby uzyskać instrukcje dotyczące oznaczania cookie elementu jako niezbędnego, zobacz Podstawowe pliki cookie.
Dostosować WebApplicationFactory
Konfigurację hosta internetowego można utworzyć niezależnie od klas testowych, poprzez dziedziczenie z WebApplicationFactory w celu utworzenia jednej lub więcej fabryk niestandardowych:
Kontekst bazy danych SUT jest zarejestrowany w jego Startup.ConfigureServices metodzie. Wywołanie zwrotne aplikacji builder.ConfigureServices testowej jest wykonywane po wykonaniu kodu aplikacji Startup.ConfigureServices . Kolejność wykonywania jest zmianą niezgodną dla Hosta ogólnego wraz z wydaniem ASP.NET Core 3.0. Aby użyć innej bazy danych do testów niż baza danych aplikacji, kontekst bazy danych aplikacji musi zostać zastąpiony w pliku builder.ConfigureServices.
W przypadku systemów testowych, które nadal korzystają z Web Host, wywołanie zwrotne aplikacji testowej builder.ConfigureServices jest wykonywane przed kodem systemu testowego Startup.ConfigureServices. Wywołanie zwrotne aplikacji testowej builder.ConfigureTestServices jest wykonywane po.
Przykładowa aplikacja znajduje deskryptor usługi dla kontekstu bazy danych i używa deskryptora do usunięcia rejestracji usługi. Następnie fabryka dodaje nowy ApplicationDbContext, który używa bazy danych w pamięci do testów.
Aby nawiązać połączenie z inną bazą danych niż baza danych w pamięci, zmień wywołanie UseInMemoryDatabase , aby połączyć kontekst z inną bazą danych. Aby użyć testowej bazy danych programu SQL Server:
Dodaj odwołanie do pakietu NuGet w pliku projektu.
Wywołaj UseSqlServer z łańcuchem połączenia do bazy danych.
Klient przykładowej aplikacji jest skonfigurowany tak, aby uniemożliwić HttpClient podążanie za przekierowaniami. Jak wyjaśniono w sekcji Mock uwierzytelnianie, pozwala to testom na sprawdzenie wyniku pierwszej odpowiedzi aplikacji. W wielu z tych testów pierwsza odpowiedź to przekierowanie z nagłówkiem Location.
Typowy test używa HttpClient oraz metod pomocniczych do przetwarzania żądania i odpowiedzi.
[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);
}
Każde żądanie POST do SUT musi spełniać kontrolę antyforgeryjną, która jest automatycznie dokonana przez system ochrony danych aplikacji. Aby zorganizować żądanie POST testu, aplikacja testowa musi:
Utwórz żądanie dla strony.
Przeanalizuj token antyforgery i cookie żądaj weryfikacji z odpowiedzi.
Wykonaj żądanie POST z tokenem antyforgery i tokenem weryfikacji żądania na miejscu cookie.
SendAsync Pomocnicze metody rozszerzenia (Helpers/HttpClientExtensions.cs) i metoda pomocnika (GetDocumentAsync) w przykładowej Helpers/HtmlHelpers.cs używają analizatora AngleSharp do obsługi sprawdzania pod kątem ochrony przed fałszerstwami za pomocą następujących metod:
GetDocumentAsync: Odbiera element HttpResponseMessage i zwraca wartość IHtmlDocument.
GetDocumentAsync używa mechanizmu, który przygotowuje wirtualną odpowiedź na podstawie oryginalnego HttpResponseMessage. Aby uzyskać więcej informacji, zobacz dokumentację AngleSharp.
SendAsync metody rozszerzenia dla HttpClient tworzą HttpRequestMessage i wywołują SendAsync(HttpRequestMessage) w celu przesłania żądań do SUT. Przeciążenia dla SendAsync akceptują formularz HTML (IHtmlFormElement) i następujące elementy:
Przycisk Prześlij formularza (IHtmlElement)
Kolekcja wartości formularza (IEnumerable<KeyValuePair<string, string>>)
Przycisk Prześlij (IHtmlElement) i wartości formularza (IEnumerable<KeyValuePair<string, string>>)
Uwaga
AngleSharp to biblioteka parsowania innej firmy używana do celów demonstracyjnych w tym temacie oraz w aplikacji przykładowej. Funkcja AngleSharp nie jest obsługiwana ani wymagana do testowania integracji aplikacji ASP.NET Core. Można użyć innych analizatorów, takich jak Pakiet Zwinności HTML (HAP). Innym podejściem jest napisanie kodu do obsługi tokenu weryfikacji żądania systemu ochrony przed fałszerstwem oraz bezpośredniego zarządzania systemem antyfałszerstwowym cookie.
Uwaga
Dostawca bazy danych EF-Core w pamięci może służyć do ograniczonego i podstawowego testowania, jednak dostawca SQLite jest zalecanym wyborem do testowania w pamięci.
Dostosowywanie klienta za pomocą programu WithWebHostBuilder
Jeśli w metodzie testowej wymagana jest dodatkowa konfiguracja, WithWebHostBuilder tworzy nowy WebApplicationFactory, zawierający IWebHostBuilder, który został dalej skonfigurowany zgodnie z potrzebami.
Metoda Post_DeleteMessageHandler_ReturnsRedirectToRoot testowa przykładowej aplikacji demonstruje użycie metody WithWebHostBuilder. Ten test wykonuje usunięcie rekordu w bazie danych, wyzwalając przesłanie formularza w SUT.
Ponieważ inny test w IndexPageTests klasie wykonuje operację, która usuwa wszystkie rekordy w bazie danych i może zostać uruchomiona przed Post_DeleteMessageHandler_ReturnsRedirectToRoot metodą, baza danych jest ponownie zainicjowana w tej metodzie testowej, aby upewnić się, że rekord jest obecny, aby SUT mógł usunąć. Wybranie pierwszego przycisku usuwania w formularzu messages w SUT jest symulowane w żądaniu do SUT:
[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);
}
Pobiera lub ustawia maksymalną liczbę odpowiedzi przekierowania, za którymi wystąpienia HttpClient powinny podążać.
7
Utwórz klasę WebApplicationFactoryClientOptions i przekaż ją do CreateClient() metody (wartości domyślne są wyświetlane w przykładzie kodu):
// 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);
Wstrzykiwanie makiety usług
Usługi można zastąpić w teście za pomocą wywołania metody ConfigureTestServices w konstruktorze hosta.
Aby wstrzyknąć pozorne usługi, SUT musi mieć klasę Startup z metodą Startup.ConfigureServices.
Przykładowy SUT zawiera usługę o zdefiniowanym zakresie, która zwraca cytat. Cytat jest osadzony w ukrytym polu na stronie indeks, gdy żądana jest strona indeks.
Services/IQuoteService.cs:
public interface IQuoteService
{
Task<string> GenerateQuote();
}
Po uruchomieniu aplikacji SUT jest generowany następujący kod:
<input id="quote" type="hidden" value="Come on, Sarah. We've an appointment in
London, and we're already 30,000 years late.">
Aby przetestować usługę i przeprowadzić iniekcję w teście integracji, usługa symulowana jest wstrzykiwana do systemu testowanego przez test. Usługa pozorowania zastępuje aplikację QuoteService usługą dostarczaną przez aplikację testową o nazwie TestQuoteService:
ConfigureTestServices zostaje wywołana, a usługa o określonym zakresie zostaje zarejestrowana:
[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);
}
Znacznik wygenerowany podczas wykonywania testu odzwierciedla tekst cudzysłowu dostarczony przez element TestQuoteService, w związku z czym asercja przechodzi.
<input id="quote" type="hidden" value="Something's interfering with time,
Mr. Scarman, and time is my business.">
Symulacja uwierzytelniania
W klasie AuthTests testy sprawdzają bezpieczeństwo punktu końcowego:
Przekierowuje nieuwierzytelnionego użytkownika do strony logowania aplikacji.
Zwraca zawartość dla uwierzytelnionego użytkownika.
W SUT na stronie /SecurePage stosuje się konwencję AuthorizePage do zastosowania elementu AuthorizeFilter na stronie. Aby uzyskać więcej informacji, zobacz Razor Konwencje autoryzacji stron.
[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);
}
Nie zezwalając klientowi na obserwowanie przekierowania, można wykonać następujące kontrole:
Kod stanu zwrócony przez SUT można sprawdzić względem oczekiwanego HttpStatusCode.Redirect wyniku, a nie końcowego kodu stanu po przekierowaniu do strony logowania, który byłby HttpStatusCode.OK.
Wartość nagłówka Location w nagłówkach odpowiedzi jest sprawdzana, aby potwierdzić, że rozpoczyna się od http://localhost/Identity/Account/Login, a nie od końcowej odpowiedzi strony logowania, gdzie nagłówek Location nie byłby obecny.
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);
}
}
Element TestAuthHandler jest wywoływany w celu uwierzytelnienia użytkownika, gdy ustawiono schemat uwierzytelniania jako Test, gdzie AddAuthentication jest zarejestrowany dla ConfigureTestServices. Ważne jest, Test aby schemat był zgodny ze schematem oczekiwanym przez aplikację. W przeciwnym razie uwierzytelnianie nie będzie działać.
Aby uzyskać więcej informacji na temat WebApplicationFactoryClientOptions, zobacz sekcję Opcje klienta.
Ustawianie środowiska
**
Domyślnie host i środowisko aplikacji SUT są domyślnie skonfigurowane na korzystanie ze środowiska deweloperskiego. Aby zastąpić środowisko SUT przy użyciu IHostBuilder:
Ustaw zmienną ASPNETCORE_ENVIRONMENT środowiskową (na przykład , StagingProduction, lub inną wartość niestandardową, taką jak Testing).
Zastąpij CreateHostBuilder w aplikacji testowej, aby odczytać zmienne środowiskowe poprzedzone prefiksem ASPNETCORE.
Jak infrastruktura testowa wywnioskuje ścieżkę główną zawartości aplikacji
Konstruktor WebApplicationFactory ustala ścieżkę katalogu głównego zawartości aplikacji poprzez wyszukiwanie elementu WebApplicationFactoryContentRootAttribute w zestawie, który zawiera testy integracyjne, z kluczem równym zestawowi TEntryPointSystem.Reflection.Assembly.FullName. Jeśli atrybut z poprawnym kluczem nie zostanie znaleziony, WebApplicationFactory wraca do wyszukiwania pliku rozwiązania (.sln) i dodaje TEntryPoint nazwę zestawu do katalogu rozwiązania. Katalog główny aplikacji (ścieżka główna zawartości) służy do odnajdywania widoków i plików zawartości.
Wyłącz kopiowanie w tle
Kopiowanie w tle powoduje wykonanie testów w innym katalogu niż katalog wyjściowy. Jeśli testy polegają na ładowaniu plików względem Assembly.Location i napotkasz problemy, może być konieczne wyłączenie kopiowania w tle.
Aby wyłączyć kopiowanie cieni przy użyciu xUnit, utwórz plik xunit.runner.json w katalogu projektu testowego z poprawnym ustawieniem konfiguracji:
{
"shadowCopy": false
}
Usuwanie obiektów
Po przeprowadzeniu testów implementacji IClassFixture, TestServer i HttpClient są usuwane, gdy xUnit usuwa WebApplicationFactory. Jeśli obiekty utworzone przez dewelopera wymagają usunięcia, należy je usunąć w implementacji IClassFixture . Aby uzyskać więcej informacji, zobacz Metoda Dispose — implementacja.
Przykład testów integracji
Przykładowa aplikacja składa się z dwóch aplikacji:
Aplikacja
Katalog projektu
opis
Aplikacja komunikatów (SUT)
src/RazorPagesProject
Umożliwia użytkownikowi dodawanie, usuwanie jednego, usuwanie wszystkich i analizowanie komunikatów.
Testowanie aplikacji
tests/RazorPagesProject.Tests
Służy do testowania integracji systemu (SUT).
Testy można uruchamiać przy użyciu wbudowanych funkcji testowych środowiska IDE, takich jak Visual Studio. Jeśli używasz programu Visual Studio Code lub wiersza polecenia, wykonaj następujące polecenie w wierszu polecenia w tests/RazorPagesProject.Tests katalogu:
dotnet test
Organizacja aplikacji wiadomości (SUT)
SUT to Razor system komunikatów Pages o następujących cechach:
Strona Indeks aplikacji (Pages/Index.cshtml i Pages/Index.cshtml.cs) udostępnia metody interfejsu użytkownika i modelu strony do kontrolowania dodawania, usuwania i analizowania komunikatów (średnie słowa na komunikat).
Komunikat jest opisany przez klasę Message (Data/Message.cs) z dwiema właściwościami: Id (klucz) i Text (komunikat). Właściwość Text jest wymagana i ograniczona do 200 znaków.
Komunikaty są przechowywane przy użyciu bazy danych w pamięci programu Entity Framework†.
Aplikacja zawiera warstwę dostępu do danych (DAL) w swojej klasie AppDbContext kontekstu bazy danych (Data/AppDbContext.cs).
Jeśli baza danych jest pusta podczas uruchamiania aplikacji, magazyn komunikatów jest inicjowany z trzema komunikatami.
Aplikacja zawiera element /SecurePage , do którego można uzyskać dostęp tylko przez uwierzytelnionego użytkownika.
† Temat ef, Test with InMemory, wyjaśnia, jak używać bazy danych w pamięci do testów przy użyciu narzędzia MSTest. W tym temacie jest używana struktura testowa xUnit . Koncepcje testów i implementacje testów w różnych platformach testowych są podobne, ale nie identyczne.
Aplikacja testowa jest aplikacją konsolową wewnątrz tests/RazorPagesProject.Tests katalogu.
Testowanie katalogu aplikacji
opis
AuthTests
Zawiera metody testowe dla:
Uzyskiwanie dostępu do bezpiecznej strony przez nieuwierzytelnionego użytkownika.
Uzyskiwanie dostępu do bezpiecznej strony przez uwierzytelnionego użytkownika z użyciem pozornego elementu AuthenticationHandler<TOptions>.
Uzyskiwanie profilu użytkownika usługi GitHub i sprawdzanie logowania użytkownika profilu.
BasicTests
Zawiera metodę testową routingu i typu zawartości.
IntegrationTests
Zawiera testy integracji dla strony Indeks przy użyciu klasy niestandardowej WebApplicationFactory .
Helpers/Utilities
Utilities.cs zawiera metodę InitializeDbForTests, używaną do zasiewania bazy danych danymi testowymi.
HtmlHelpers.cs Udostępnia metodę zwracania obiektu AngleSharp IHtmlDocument do użytku przez metody testowe.
HttpClientExtensions.cs zapewnij przeciążenia dla SendAsync aby przesyłać żądania do SUT.
Framework testowy to xUnit. Testy integracji są przeprowadzane przy użyciu Microsoft.AspNetCore.TestHost, który zawiera TestServer. Ponieważ pakiet Microsoft.AspNetCore.Mvc.Testing jest używany do konfigurowania hosta testowego i serwera testowego, pakiety TestHost i TestServer nie wymagają bezpośrednich odwołań w pliku projektu aplikacji testowej ani ręcznej konfiguracji przez dewelopera w tej aplikacji.
Testy integracji zwykle wymagają małego zestawu danych w bazie danych przed wykonaniem testu. Na przykład test usuwania wywołuje usunięcie rekordu bazy danych, więc baza danych musi mieć co najmniej jeden rekord, aby żądanie usunięcia powiodło się.
Przykładowa aplikacja zapełnia bazę danych trzema komunikatami w Utilities.cs, które mogą być używane przez testy podczas ich wykonywania:
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." }
};
}
Kontekst bazy danych SUT jest zarejestrowany w jego Startup.ConfigureServices metodzie. Wywołanie zwrotne aplikacji builder.ConfigureServices testowej jest wykonywane po wykonaniu kodu aplikacji Startup.ConfigureServices . Aby użyć innej bazy danych do testów, kontekst bazy danych aplikacji musi zostać zastąpiony w pliku builder.ConfigureServices. Aby uzyskać więcej informacji, zobacz sekcję Customize WebApplicationFactory.
W przypadku systemów testowych, które nadal korzystają z Web Host, wywołanie zwrotne aplikacji testowej builder.ConfigureServices jest wykonywane przed kodem systemu testowego Startup.ConfigureServices. Wywołanie zwrotne aplikacji testowej builder.ConfigureTestServices jest wykonywane po.
Do testowania spAs zalecamy narzędzie, takie jak Playwright dla platformy .NET, które może zautomatyzować przeglądarkę.
Wprowadzenie do testów integracji
Testy integracji oceniają składniki aplikacji na szerszym poziomie niż testy jednostkowe. Testy jednostkowe służą do testowania izolowanych składników oprogramowania, takich jak poszczególne metody klasy. Testy integracji potwierdzają, że co najmniej dwa składniki aplikacji współpracują ze sobą, aby wygenerować oczekiwany wynik, prawdopodobnie włącznie z każdym składnikiem wymaganym do pełnego przetworzenia żądania.
Te szersze testy służą do testowania infrastruktury aplikacji i całej struktury, często w tym następujących składników:
baza danych
System plików
Urządzenia sieciowe
Przepływ żądań i odpowiedzi
Testy jednostkowe używają sfabrykowanych składników, znanych jako fałszywe lub makiety obiektów, zamiast składników infrastruktury.
W przeciwieństwie do testów jednostkowych, testy integracji:
Użyj rzeczywistych składników używanych przez aplikację w środowisku produkcyjnym.
Wymagaj więcej kodu i przetwarzania danych.
Trwa dłużej, aby się uruchomić.
W związku z tym ogranicz użycie testów integracji do najważniejszych scenariuszy infrastruktury. Jeśli zachowanie można przetestować przy użyciu testu jednostkowego lub testu integracji, wybierz test jednostkowy.
W dyskusjach na temat testów integracyjnych testowany projekt jest często nazywany Systemem Pod Testami, lub w skrócie "SUT". Element "SUT" jest używany w tym artykule do odwoływania się do testowanej aplikacji ASP.NET Core.
Nie zapisuj testów integracji dla każdej permutacji danych i dostępu do plików za pomocą baz danych i systemów plików. Niezależnie od liczby miejsc w aplikacji współdziała z bazami danych i systemami plików, skoncentrowany zestaw testów integracji odczytu, zapisu, aktualizacji i usuwania zwykle umożliwia odpowiednie testowanie składników bazy danych i systemu plików. Używaj testów jednostkowych do rutynowych testów logiki metod, które wchodzą w interakcje z tymi składnikami. W testach jednostkowych użycie fałszywych elementów infrastruktury lub imitacji przyspiesza wykonywanie testów.
testy integracji ASP.NET Core
Testy integracji w programie ASP.NET Core wymagają następujących elementów:
Projekt testowy służy do zawierania i wykonywania testów. Projekt testowy zawiera odwołanie do systemu testowanego.
Projekt testowy tworzy serwer testowy dla SUT i korzysta z klienta serwera testowego do obsługi żądań i odpowiedzi z SUT.
Moduł uruchamiający testy służy do wykonywania testów i raportowania wyników testów.
Testy integracji są zgodne z sekwencją zdarzeń obejmującą zwykłe kroki testowe: Rozmieszczenie, Działanie i Sprawdzenie.
Host internetowy SUT jest skonfigurowany.
Klient serwera testowego jest tworzony w celu przesyłania żądań do aplikacji.
Etap Aranżacji testu jest realizowany: aplikacja testowa przygotowuje żądanie.
Krok testu Act jest wykonywany: klient przesyła żądanie i odbiera odpowiedź.
Krok testu potwierdzenia jest wykonywany: rzeczywista odpowiedź jest weryfikowana jako powodzenie lub niepowodzenie w oparciu o oczekiwaną odpowiedź.
Proces będzie kontynuowany do momentu wykonania wszystkich testów.
Wyniki testów są zgłaszane.
Zazwyczaj testowy host internetowy jest skonfigurowany inaczej niż normalny host internetowy aplikacji na potrzeby przebiegów testu. Na przykład do testów mogą być używane różne ustawienia bazy danych lub różnych aplikacji.
Składniki infrastruktury, takie jak testowy host internetowy i serwer testowy w pamięci (TestServer), są dostarczane lub zarządzane przez pakiet Microsoft.AspNetCore.Mvc.Testing . Użycie tego pakietu usprawnia tworzenie i wykonywanie testów.
Kopiuje plik zależności (.deps) z sutu do katalogu projektu testowego bin .
Ustawia katalog główny zawartości na katalog główny projektu SUT, tak aby pliki statyczne i strony/widoki zostały znalezione podczas wykonywania testów.
Udostępnia klasę WebApplicationFactory , aby usprawnić uruchamianie aplikacji SUT za pomocą polecenia TestServer.
W dokumentacji testów jednostkowych opisano sposób konfigurowania projektu testowego i modułu uruchamiającego testy oraz szczegółowe instrukcje dotyczące uruchamiania testów i zaleceń dotyczących sposobu nazywania testów i klas testowych.
Oddziel testy jednostkowe od testów integracji do różnych projektów. Oddzielanie testów:
Pomaga zagwarantować, że składniki testowania infrastruktury nie zostaną przypadkowo uwzględnione w testach jednostkowych.
Umożliwia kontrolę nad tym, który zestaw testów jest uruchamiany.
Praktycznie nie ma różnicy między konfiguracją testów Razor aplikacji Pages i aplikacji MVC. Jedyną różnicą jest sposób, w jaki testy są nazwane.
Razor W aplikacji Pages testy punktów końcowych strony są zwykle nazwane po klasie modelu strony (na przykład IndexPageTests w celu przetestowania integracji składników dla strony Indeks). W aplikacji MVC testy są zwykle zorganizowane według klas kontrolerów i nazwane po testowych kontrolerach (na przykład HomeControllerTests do testowania integracji składników dla Home kontrolera).
Określ zestaw Web SDK w pliku projektu (<Project Sdk="Microsoft.NET.Sdk.Web">).
Te wymagania wstępne można zobaczyć w przykładowej aplikacji.
tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj Sprawdź plik. Przykładowa aplikacja używa struktury testowej xUnit i biblioteki analizatora AngleSharp , więc przykładowa aplikacja również odwołuje się do:
Klasy testowe implementują interfejs fixture klasy (IClassFixture), aby wskazać, że klasa zawiera testy i udostępnia wspólne wystąpienia obiektów między testami w klasie.
Następująca klasa testowa BasicTests używa WebApplicationFactory do uruchomienia SUT i dostarczenia HttpClient do metody testowej Get_EndpointsReturnSuccessAndCorrectContentType. Metoda sprawdza, czy kod stanu odpowiedzi jest pomyślny (200–299), a nagłówek ma wartość Content-Type na kilku stronach aplikacji.
CreateClient() Program tworzy wystąpienie HttpClient , które automatycznie śledzi przekierowania i obsługuje pliki cookie.
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());
}
}
Domyślnie pliki cookie inne niż podstawowe nie są zachowywane w żądaniach, gdy zasady zgody ogólnego rozporządzenia o ochronie danych są włączone. Aby zachować nieistotne pliki cookie, takie jak te używane przez dostawcę TempData, oznacz je jako niezbędne w testach. Aby uzyskać instrukcje dotyczące oznaczania cookie elementu jako niezbędnego, zobacz Podstawowe pliki cookie.
AngleSharp vs Application Parts kontra antyfałszerstwo
W tym artykule użyto analizatora AngleSharp do obsługi testów antyforgeryjnych przez ładowanie stron i analizowanie kodu HTML. Aby przetestować punkty końcowe kontrolera i widoki stron Razor na niższym poziomie, bez martwienia się o sposób renderowania w przeglądarce, rozważ użycie Application Parts. Metoda Części aplikacji wprowadza do aplikacji kontroler lub Razor stronę, która może być używana do składania żądań JSON w celu pozyskania wymaganych wartości. Aby uzyskać więcej informacji, zobacz blog Testowanie integracji zasobów ASP.NET Core chronionych przed fałszerstwami przy użyciu części aplikacji oraz powiązane repozytorium GitHub autorstwa Martina Costello.
Dostosować WebApplicationFactory
Konfigurację hosta sieciowego można utworzyć niezależnie od klas testowych, dziedzicząc z WebApplicationFactory<TEntryPoint> w celu utworzenia jednej lub więcej fabryk niestandardowych.
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");
}
}
Kontekst bazy danych SUT jest zarejestrowany w pliku Program.cs. Wywołanie zwrotne aplikacji testowej builder.ConfigureServices jest wykonywane po wykonaniu kodu aplikacji Program.cs. Aby użyć innej bazy danych do testów niż baza danych aplikacji, kontekst bazy danych aplikacji musi zostać zastąpiony w pliku builder.ConfigureServices.
Przykładowa aplikacja znajduje deskryptor usługi dla kontekstu bazy danych i używa deskryptora do usunięcia rejestracji usługi. Następnie fabryka dodaje nowy ApplicationDbContext , który używa bazy danych w pamięci do testów.
Aby nawiązać połączenie z inną bazą danych, zmień wartość DbConnection. Aby użyć testowej bazy danych programu SQL Server:
Dodaj odwołanie do pakietu NuGet w pliku projektu.
Zadzwoń UseInMemoryDatabase.
Użyj niestandardowego CustomWebApplicationFactory w klasach testowych. W poniższym przykładzie użyto fabryki w klasie IndexPageTests.
Klient przykładowej aplikacji jest skonfigurowany tak, aby uniemożliwić HttpClient podążanie za przekierowaniami. Jak wyjaśniono w sekcji Mock uwierzytelnianie, pozwala to testom na sprawdzenie wyniku pierwszej odpowiedzi aplikacji. We wielu z tych testów, pierwsza odpowiedź jest przekierowaniem z nagłówkiem Location.
Typowy test używa HttpClient oraz metod pomocniczych do przetwarzania żądania i odpowiedzi.
[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);
}
Każde żądanie POST do SUT musi spełniać kontrolę antyforgeryjną, która jest automatycznie dokonana przez system ochrony danych aplikacji. Aby zorganizować żądanie POST testu, aplikacja testowa musi:
Utwórz żądanie dla strony.
Przeanalizuj token antyforgery i cookie żądaj weryfikacji z odpowiedzi.
Utwórz żądanie POST z tokenem antyforgery i cookie zażądaj weryfikacji.
SendAsync Pomocnicze metody rozszerzenia (Helpers/HttpClientExtensions.cs) i metoda pomocnika (GetDocumentAsync) w przykładowej Helpers/HtmlHelpers.cs używają analizatora AngleSharp do obsługi sprawdzania pod kątem ochrony przed fałszerstwami za pomocą następujących metod:
GetDocumentAsync: Odbiera element HttpResponseMessage i zwraca wartość IHtmlDocument.
GetDocumentAsync używa mechanizmu, który przygotowuje wirtualną odpowiedź na podstawie oryginalnego HttpResponseMessage. Aby uzyskać więcej informacji, zobacz dokumentację AngleSharp.
SendAsync metody rozszerzenia dla HttpClient tworzą HttpRequestMessage i wywołują SendAsync(HttpRequestMessage) w celu przesłania żądań do SUT. Przeciążenia dla SendAsync akceptują formularz HTML (IHtmlFormElement) i następujące elementy:
Przycisk Prześlij formularza (IHtmlElement)
Kolekcja wartości formularza (IEnumerable<KeyValuePair<string, string>>)
Przycisk Prześlij (IHtmlElement) i wartości formularza (IEnumerable<KeyValuePair<string, string>>)
AngleSharp to biblioteka do analizy od strony trzeciej używana do celów demonstracyjnych w tym artykule i przykładowej aplikacji. Funkcja AngleSharp nie jest obsługiwana ani wymagana do testowania integracji aplikacji ASP.NET Core. Można użyć innych analizatorów, takich jak Pakiet Zwinności HTML (HAP). Innym podejściem jest napisanie kodu do obsługi tokenu weryfikacji żądania systemu ochrony przed fałszerstwem oraz bezpośredniego zarządzania systemem antyfałszerstwowym cookie. Aby uzyskać więcej informacji, zobacz AngleSharp kontra Application Parts do weryfikacji antyfałszerstw w tym artykule.
Dostawca bazy danych EF-Core w pamięci może służyć do ograniczonego i podstawowego testowania, jednak dostawca SQLite jest zalecanym wyborem do testowania w pamięci.
Dostosowywanie klienta za pomocą programu WithWebHostBuilder
Jeśli w metodzie testowej wymagana jest dodatkowa konfiguracja, WithWebHostBuilder tworzy nowy WebApplicationFactory, zawierający IWebHostBuilder, który został dalej skonfigurowany zgodnie z potrzebami.
Przykładowy kod wywołuje aby zastąpić skonfigurowane usługi próbkami testowymi. Aby uzyskać więcej informacji i przykłady użycia, zapoznaj się z sekcją Wstrzykiwanie usług mock w tym artykule.
Metoda Post_DeleteMessageHandler_ReturnsRedirectToRoot testowa przykładowej aplikacji demonstruje użycie metody WithWebHostBuilder. Ten test wykonuje usunięcie rekordu w bazie danych, wyzwalając przesłanie formularza w SUT.
Ponieważ inny test w IndexPageTests klasie wykonuje operację, która usuwa wszystkie rekordy w bazie danych i może zostać uruchomiona przed Post_DeleteMessageHandler_ReturnsRedirectToRoot metodą, baza danych jest ponownie zainicjowana w tej metodzie testowej, aby upewnić się, że rekord jest obecny, aby SUT mógł usunąć. Wybranie pierwszego przycisku usuwania w formularzu messages w SUT jest symulowane w żądaniu do SUT:
[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);
}
Opcje klienta
Zobacz stronę WebApplicationFactoryClientOptions dotyczącą ustawień domyślnych i dostępnych opcji podczas tworzenia instancji HttpClient.
Utwórz klasę WebApplicationFactoryClientOptions i przekaż ją do CreateClient() metody :
UWAGA: Aby uniknąć ostrzeżeń przekierowania HTTPS w dziennikach podczas korzystania z middleware do przekierowań HTTPS, ustaw BaseAddress = new Uri("https://localhost")
Wstrzykiwanie usług testowych
Usługi można zastąpić w teście za pomocą wywołania metody ConfigureTestServices w konstruktorze hosta. Aby ograniczyć zakres nadpisanych usług do samego testu, metoda WithWebHostBuilder jest używana w celu uzyskania konstruktora hosta. Można to zobaczyć w następujących testach:
Przykładowy SUT zawiera usługę o zdefiniowanym zakresie, która zwraca cytat. Cytat jest osadzony w ukrytym polu na stronie indeks, gdy żądana jest strona indeks.
Services/IQuoteService.cs:
public interface IQuoteService
{
Task<string> GenerateQuote();
}
Po uruchomieniu aplikacji SUT jest generowany następujący kod:
<input id="quote" type="hidden" value="Come on, Sarah. We've an appointment in
London, and we're already 30,000 years late.">
Aby przetestować usługę i przeprowadzić iniekcję w teście integracji, usługa symulowana jest wstrzykiwana do systemu testowanego przez test. Usługa pozorowania zastępuje aplikację QuoteService usługą dostarczaną przez aplikację testową o nazwie TestQuoteService:
ConfigureTestServices zostaje wywołana, a usługa o określonym zakresie zostaje zarejestrowana:
[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);
}
Znacznik wygenerowany podczas wykonywania testu odzwierciedla tekst cudzysłowu dostarczony przez element TestQuoteService, w związku z czym asercja przechodzi.
<input id="quote" type="hidden" value="Something's interfering with time,
Mr. Scarman, and time is my business.">
Symulacja uwierzytelniania
Testy w AuthTests klasie sprawdzają, czy zabezpieczony punkt końcowy:
Przekierowuje nieuwierzytelnionego użytkownika do strony logowania aplikacji.
Zwraca zawartość dla uwierzytelnionego użytkownika.
Na stronie SUT /SecurePage jest używana konwencja AuthorizePage do zastosowania elementu AuthorizeFilter na stronie. Aby uzyskać więcej informacji, zobacz Razor Konwencje autoryzacji stron.
[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);
}
Nie zezwalając klientowi na obserwowanie przekierowania, można wykonać następujące kontrole:
Kod stanu zwrócony przez SUT można sprawdzić względem oczekiwanego HttpStatusCode.Redirect wyniku, a nie końcowego kodu stanu po przekierowaniu do strony logowania, który byłby HttpStatusCode.OK.
Wartość nagłówka Location w nagłówkach odpowiedzi jest sprawdzana, aby potwierdzić, że rozpoczyna się od http://localhost/Identity/Account/Login, a nie od odpowiedzi końcowej strony logowania, gdzie nagłówek Location nie byłby obecny.
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);
}
}
Element TestAuthHandler jest wywoływany w celu uwierzytelnienia użytkownika, gdy ustawiono schemat uwierzytelniania jako TestScheme, gdzie AddAuthentication jest zarejestrowany dla ConfigureTestServices. Ważne jest, TestScheme aby schemat był zgodny ze schematem oczekiwanym przez aplikację. W przeciwnym razie uwierzytelnianie nie będzie działać.
Zobacz to repozytorium GitHub, aby zapoznać się z podstawowymi testami oprogramowania pośredniczącego uwierzytelniania. Zawiera on serwer testowy specyficzny dla scenariusza testowego.
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");
}
}
Jak infrastruktura testowa wywnioskuje ścieżkę główną zawartości aplikacji
Konstruktor WebApplicationFactory ustala ścieżkę katalogu głównego zawartości aplikacji poprzez wyszukiwanie elementu WebApplicationFactoryContentRootAttribute w zestawie, który zawiera testy integracyjne, z kluczem równym zestawowi TEntryPointSystem.Reflection.Assembly.FullName. Jeśli atrybut z poprawnym kluczem nie zostanie znaleziony, WebApplicationFactory wraca do wyszukiwania pliku rozwiązania (.sln) i dodaje TEntryPoint nazwę zestawu do katalogu rozwiązania. Katalog główny aplikacji (ścieżka główna zawartości) służy do odnajdywania widoków i plików zawartości.
Wyłącz kopiowanie w tle
Kopiowanie w tle powoduje wykonanie testów w innym katalogu niż katalog wyjściowy. Jeśli testy polegają na ładowaniu plików względem Assembly.Location i napotkasz problemy, może być konieczne wyłączenie kopiowania w tle.
Aby wyłączyć kopiowanie cieni przy użyciu xUnit, utwórz plik xunit.runner.json w katalogu projektu testowego z poprawnym ustawieniem konfiguracji:
{
"shadowCopy": false
}
Usuwanie obiektów
Po przeprowadzeniu testów implementacji IClassFixture, TestServer i HttpClient są usuwane, gdy xUnit usuwa WebApplicationFactory. Jeśli obiekty utworzone przez dewelopera wymagają usunięcia, należy je usunąć w implementacji IClassFixture . Aby uzyskać więcej informacji, zobacz Metoda Dispose — implementacja.
Przykład testów integracji
Przykładowa aplikacja składa się z dwóch aplikacji:
Aplikacja
Katalog projektu
opis
Aplikacja komunikatów (SUT)
src/RazorPagesProject
Umożliwia użytkownikowi dodawanie, usuwanie jednego, usuwanie wszystkich i analizowanie komunikatów.
Testowanie aplikacji
tests/RazorPagesProject.Tests
Służy do testowania integracji systemu (SUT).
Testy można uruchamiać przy użyciu wbudowanych funkcji testowych środowiska IDE, takich jak Visual Studio. Jeśli używasz programu Visual Studio Code lub wiersza polecenia, wykonaj następujące polecenie w wierszu polecenia w tests/RazorPagesProject.Tests katalogu:
dotnet test
Organizacja aplikacji wiadomości (SUT)
SUT to Razor system komunikatów Pages o następujących cechach:
Strona Indeks aplikacji (Pages/Index.cshtml i Pages/Index.cshtml.cs) udostępnia metody interfejsu użytkownika i modelu strony do kontrolowania dodawania, usuwania i analizowania komunikatów (średnie słowa na komunikat).
Komunikat jest opisany przez klasę Message (Data/Message.cs) z dwiema właściwościami: Id (klucz) i Text (komunikat). Właściwość Text jest wymagana i ograniczona do 200 znaków.
Komunikaty są przechowywane przy użyciu bazy danych w pamięci programu Entity Framework†.
Aplikacja zawiera warstwę dostępu do danych (DAL) w swojej klasie AppDbContext kontekstu bazy danych (Data/AppDbContext.cs).
Jeśli baza danych jest pusta podczas uruchamiania aplikacji, magazyn komunikatów jest inicjowany z trzema komunikatami.
Aplikacja zawiera element /SecurePage , do którego można uzyskać dostęp tylko przez uwierzytelnionego użytkownika.
† Artykuł EF, Test z InMemory, wyjaśnia, jak używać bazy danych w pamięci do testów przy użyciu narzędzia MSTest. W tym temacie jest używana struktura testowa xUnit . Koncepcje testów i implementacje testów w różnych platformach testowych są podobne, ale nie identyczne.
Aplikacja testowa jest aplikacją konsolową wewnątrz tests/RazorPagesProject.Tests katalogu.
Testowanie katalogu aplikacji
opis
AuthTests
Zawiera metody testowe dla:
Uzyskiwanie dostępu do bezpiecznej strony przez nieuwierzytelnionego użytkownika.
Uzyskiwanie dostępu do bezpiecznej strony przez uwierzytelnionego użytkownika z użyciem pozornego elementu AuthenticationHandler<TOptions>.
Uzyskiwanie profilu użytkownika usługi GitHub i sprawdzanie logowania użytkownika profilu.
BasicTests
Zawiera metodę testową routingu i typu zawartości.
IntegrationTests
Zawiera testy integracji dla strony Indeks przy użyciu klasy niestandardowej WebApplicationFactory .
Helpers/Utilities
Utilities.cs zawiera metodę InitializeDbForTests, używaną do zasiewania bazy danych danymi testowymi.
HtmlHelpers.cs Udostępnia metodę zwracania obiektu AngleSharp IHtmlDocument do użytku przez metody testowe.
HttpClientExtensions.cs zapewnij przeciążenia dla SendAsync aby przesyłać żądania do SUT.
Framework testowy to xUnit. Testy integracji są przeprowadzane przy użyciu Microsoft.AspNetCore.TestHost, który zawiera TestServer. Ponieważ pakiet Microsoft.AspNetCore.Mvc.Testing jest używany do konfigurowania hosta testowego i serwera testowego, pakiety TestHost i TestServer nie wymagają bezpośrednich odwołań w pliku projektu aplikacji testowej ani ręcznej konfiguracji przez dewelopera w tej aplikacji.
Testy integracji zwykle wymagają małego zestawu danych w bazie danych przed wykonaniem testu. Na przykład test usuwania wywołuje usunięcie rekordu bazy danych, więc baza danych musi mieć co najmniej jeden rekord, aby żądanie usunięcia powiodło się.
Przykładowa aplikacja zapełnia bazę danych trzema komunikatami w Utilities.cs, które mogą być używane przez testy podczas ich wykonywania:
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." }
};
}
Kontekst bazy danych SUT jest zarejestrowany w pliku Program.cs. Wywołanie zwrotne aplikacji builder.ConfigureServices testowej jest wykonywane po wykonaniu kodu aplikacji Program.cs . Aby użyć innej bazy danych do testów, kontekst bazy danych aplikacji musi zostać zastąpiony w pliku builder.ConfigureServices. Aby uzyskać więcej informacji, zobacz sekcję Customize WebApplicationFactory.
Do testowania spAs zalecamy narzędzie, takie jak Playwright dla platformy .NET, które może zautomatyzować przeglądarkę.
Wprowadzenie do testów integracji
Testy integracji oceniają składniki aplikacji na szerszym poziomie niż testy jednostkowe. Testy jednostkowe służą do testowania izolowanych składników oprogramowania, takich jak poszczególne metody klasy. Testy integracji potwierdzają, że co najmniej dwa składniki aplikacji współpracują ze sobą, aby wygenerować oczekiwany wynik, prawdopodobnie włącznie z każdym składnikiem wymaganym do pełnego przetworzenia żądania.
Te szersze testy służą do testowania infrastruktury aplikacji i całej struktury, często w tym następujących składników:
baza danych
System plików
Urządzenia sieciowe
Przepływ żądań i odpowiedzi
Testy jednostkowe używają sfabrykowanych składników, znanych jako fałszywe lub makiety obiektów, zamiast składników infrastruktury.
W przeciwieństwie do testów jednostkowych, testy integracji:
Użyj rzeczywistych składników używanych przez aplikację w środowisku produkcyjnym.
Wymagaj więcej kodu i przetwarzania danych.
Uruchamianie trwa dłużej.
W związku z tym ogranicz użycie testów integracji do najważniejszych scenariuszy infrastruktury. Jeśli zachowanie można przetestować przy użyciu testu jednostkowego lub testu integracji, wybierz test jednostkowy.
W dyskusjach na temat testów integracyjnych testowany projekt jest często nazywany Systemem Pod Testami, lub w skrócie "SUT". Element "SUT" jest używany w tym artykule do odwoływania się do testowanej aplikacji ASP.NET Core.
Nie zapisuj testów integracji dla każdej permutacji danych i dostępu do plików za pomocą baz danych i systemów plików. Niezależnie od liczby miejsc w aplikacji współdziała z bazami danych i systemami plików, skoncentrowany zestaw testów integracji odczytu, zapisu, aktualizacji i usuwania zwykle umożliwia odpowiednie testowanie składników bazy danych i systemu plików. Używaj testów jednostkowych do rutynowych testów logiki metod, które wchodzą w interakcje z tymi składnikami. W testach jednostkowych użycie fałszywych elementów infrastruktury lub imitacji przyspiesza wykonywanie testów.
testy integracji ASP.NET Core
Testy integracji w programie ASP.NET Core wymagają następujących elementów:
Projekt testowy służy do zawierania i wykonywania testów. Projekt testowy zawiera odwołanie do SUT.
Projekt testowy tworzy serwer testowy dla SUT i korzysta z klienta serwera testowego do obsługi żądań i odpowiedzi z SUT.
Runner testów służy do wykonywania testów i raportowania wyników testu.
Testy integracji są zgodne z sekwencją zdarzeń obejmującą zwykłe kroki testowe: Rozmieszczenie, Działanie i Sprawdzenie.
Host internetowy SUT jest skonfigurowany.
Klient serwera testowego jest tworzony w celu przesyłania żądań do aplikacji.
Etap Aranżacji testu jest realizowany: aplikacja testowa przygotowuje żądanie.
Krok testu Act jest wykonywany: klient przesyła żądanie i odbiera odpowiedź.
Krok testu potwierdzenia jest wykonywany: rzeczywista odpowiedź jest weryfikowana jako powodzenie lub niepowodzenie w oparciu o oczekiwaną odpowiedź.
Proces będzie kontynuowany do momentu wykonania wszystkich testów.
Wyniki testów są raportowane.
Zazwyczaj testowy host internetowy jest skonfigurowany inaczej niż normalny host internetowy aplikacji na potrzeby przebiegów testu. Na przykład do testów mogą być używane różne ustawienia bazy danych lub różnych aplikacji.
Składniki infrastruktury, takie jak testowy host internetowy i serwer testowy w pamięci (TestServer), są dostarczane lub zarządzane przez pakiet Microsoft.AspNetCore.Mvc.Testing . Użycie tego pakietu usprawnia tworzenie i wykonywanie testów.
Kopiuje plik zależności (.deps) z sutu do katalogu projektu testowego bin .
Ustawia katalog główny zawartości na katalog główny projektu SUT, tak aby pliki statyczne i strony/widoki zostały znalezione podczas wykonywania testów.
Udostępnia klasę WebApplicationFactory , aby usprawnić uruchamianie aplikacji SUT za pomocą polecenia TestServer.
W dokumentacji testów jednostkowych opisano sposób konfigurowania projektu testowego i modułu uruchamiającego testy oraz szczegółowe instrukcje dotyczące uruchamiania testów i zaleceń dotyczących sposobu nazywania testów i klas testowych.
Oddziel testy jednostkowe od testów integracji do różnych projektów. Oddzielanie testów:
Pomaga zagwarantować, że składniki testowania infrastruktury nie zostaną przypadkowo uwzględnione w testach jednostkowych.
Umożliwia kontrolę nad tym, który zestaw testów jest uruchamiany.
Praktycznie nie ma różnicy między konfiguracją testów Razor aplikacji Pages i aplikacji MVC. Jedyną różnicą jest sposób, w jaki testy są nazwane.
Razor W aplikacji Pages testy punktów końcowych strony są zwykle nazwane po klasie modelu strony (na przykład IndexPageTests w celu przetestowania integracji składników dla strony Indeks). W aplikacji MVC testy są zwykle zorganizowane według klas kontrolerów i nazwane po testowych kontrolerach (na przykład HomeControllerTests do testowania integracji składników dla Home kontrolera).
Określ zestaw Web SDK w pliku projektu (<Project Sdk="Microsoft.NET.Sdk.Web">).
Te wymagania wstępne można zobaczyć w przykładowej aplikacji. Sprawdź plik tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj. Przykładowa aplikacja używa struktury testowej xUnit i biblioteki analizatora AngleSharp , więc przykładowa aplikacja również odwołuje się do:
Klasy testowe implementują interfejs fixture klasy (IClassFixture), aby wskazać, że klasa zawiera testy i udostępnia wspólne wystąpienia obiektów między testami w klasie.
Następująca klasa testowa BasicTests używa WebApplicationFactory do uruchomienia SUT i dostarczenia HttpClient do metody testowej Get_EndpointsReturnSuccessAndCorrectContentType. Metoda sprawdza, czy kod stanu odpowiedzi jest pomyślny (200–299), a nagłówek ma wartość Content-Type na kilku stronach aplikacji.
CreateClient() Program tworzy wystąpienie HttpClient , które automatycznie śledzi przekierowania i obsługuje pliki cookie.
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());
}
}
Domyślnie pliki cookie inne niż podstawowe nie są zachowywane w żądaniach, gdy zasady zgody ogólnego rozporządzenia o ochronie danych są włączone. Aby zachować nieistotne pliki cookie, takie jak te używane przez dostawcę TempData, oznacz je jako niezbędne w testach. Aby uzyskać instrukcje dotyczące oznaczania cookie elementu jako niezbędnego, zobacz Podstawowe pliki cookie.
AngleSharp vs Application Parts kontra antyfałszerstwo
W tym artykule użyto analizatora AngleSharp do obsługi testów antyforgeryjnych przez ładowanie stron i analizowanie kodu HTML. Aby przetestować punkty końcowe kontrolera i widoki stron Razor na niższym poziomie, bez martwienia się o sposób renderowania w przeglądarce, rozważ użycie Application Parts. Metoda Części aplikacji wprowadza do aplikacji kontroler lub Razor stronę, które mogą służyć do tworzenia żądań JSON w celu uzyskania wymaganych wartości. Aby uzyskać więcej informacji, zobacz blog Testowanie integracji zasobów ASP.NET Core chronionych przed fałszerstwami przy użyciu części aplikacji oraz powiązane repozytorium GitHub autorstwa Martina Costello.
Dostosować WebApplicationFactory
Konfigurację hosta internetowego można utworzyć niezależnie od klas testowych, dziedzicząc z WebApplicationFactory<TEntryPoint> w celu utworzenia co najmniej jednej fabryki niestandardowej:
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");
}
}
Kontekst bazy danych SUT jest zarejestrowany w pliku Program.cs. Wywołanie zwrotne aplikacji builder.ConfigureServices testowej jest wykonywane po wykonaniu kodu aplikacji Program.cs. Aby użyć innej bazy danych do testów niż baza danych aplikacji, kontekst bazy danych aplikacji musi zostać zastąpiony w pliku builder.ConfigureServices.
Przykładowa aplikacja znajduje deskryptor usługi dla kontekstu bazy danych i używa deskryptora do usunięcia rejestracji usługi. Następnie fabryka dodaje nowy ApplicationDbContext , który używa bazy danych w pamięci do testów.
Aby nawiązać połączenie z inną bazą danych, zmień wartość DbConnection. Aby użyć testowej bazy danych programu SQL Server:
Dodaj odwołanie do pakietu NuGet w pliku projektu.
Zadzwoń UseInMemoryDatabase.
Użyj niestandardowego CustomWebApplicationFactory w klasach testowych. W poniższym przykładzie użyto fabryki w klasie IndexPageTests.
Klient przykładowej aplikacji jest skonfigurowany tak, aby HttpClient nie podążał za przekierowaniami. Jak wyjaśniono w sekcji Mock uwierzytelnianie, pozwala to testom na sprawdzenie wyniku pierwszej odpowiedzi aplikacji. Pierwsza odpowiedź w wielu z tych testów to przekierowanie z nagłówkiem Location.
Typowy test używa HttpClient oraz metod pomocniczych do przetwarzania żądania i odpowiedzi.
[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);
}
Każde żądanie POST do SUT musi spełniać kontrolę antyforgeryjną, która jest automatycznie dokonana przez system ochrony danych aplikacji. Aby zorganizować żądanie POST testu, aplikacja testowa musi:
Utwórz żądanie dla strony.
Przeanalizuj token antyforgery i cookie żądaj weryfikacji z odpowiedzi.
Utwórz żądanie POST z tokenem antyforgery i cookie zażądaj weryfikacji.
SendAsync Pomocnicze metody rozszerzenia (Helpers/HttpClientExtensions.cs) i metoda pomocnika (GetDocumentAsync) w przykładowej Helpers/HtmlHelpers.cs używają analizatora AngleSharp do obsługi sprawdzania pod kątem ochrony przed fałszerstwami za pomocą następujących metod:
GetDocumentAsync: Odbiera element HttpResponseMessage i zwraca wartość IHtmlDocument.
GetDocumentAsync używa mechanizmu, który przygotowuje wirtualną odpowiedź na podstawie oryginalnego HttpResponseMessage. Aby uzyskać więcej informacji, zobacz dokumentację AngleSharp.
SendAsync metody rozszerzenia dla HttpClient tworzą HttpRequestMessage i wywołują SendAsync(HttpRequestMessage) w celu przesłania żądań do SUT. Przeciążenia dla SendAsync akceptują formularz HTML (IHtmlFormElement) i następujące elementy:
Przycisk Prześlij formularza (IHtmlElement)
Kolekcja wartości formularza (IEnumerable<KeyValuePair<string, string>>)
Przycisk Prześlij (IHtmlElement) i wartości formularza (IEnumerable<KeyValuePair<string, string>>)
AngleSharp to biblioteka do analizy od strony trzeciej używana do celów demonstracyjnych w tym artykule i przykładowej aplikacji. Funkcja AngleSharp nie jest obsługiwana ani wymagana do testowania integracji aplikacji ASP.NET Core. Można użyć innych analizatorów, takich jak Pakiet Zwinności HTML (HAP). Innym podejściem jest napisanie kodu do obsługi tokenu weryfikacji żądania systemu ochrony przed fałszerstwem oraz bezpośredniego zarządzania systemem antyfałszerstwowym cookie. Aby uzyskać więcej informacji, zobacz AngleSharp kontra Application Parts do weryfikacji antyfałszerstw w tym artykule.
Dostawca bazy danych EF-Core w pamięci może służyć do ograniczonego i podstawowego testowania, jednak dostawca SQLite jest zalecanym wyborem do testowania w pamięci.
Dostosowywanie klienta za pomocą programu WithWebHostBuilder
Jeśli w metodzie testowej wymagana jest dodatkowa konfiguracja, WithWebHostBuilder tworzy nowy WebApplicationFactory, zawierający IWebHostBuilder, który został dalej skonfigurowany zgodnie z potrzebami.
Przykładowy kod wywołuje aby zastąpić skonfigurowane usługi próbkami testowymi. Aby uzyskać więcej informacji i przykłady użycia, zapoznaj się z sekcją Wstrzykiwanie usług mock w tym artykule.
Metoda Post_DeleteMessageHandler_ReturnsRedirectToRoot testowa przykładowej aplikacji demonstruje użycie metody WithWebHostBuilder. Ten test wykonuje usunięcie rekordu w bazie danych, wyzwalając przesłanie formularza w SUT.
Ponieważ inny test w IndexPageTests klasie wykonuje operację, która usuwa wszystkie rekordy w bazie danych i może zostać uruchomiona przed Post_DeleteMessageHandler_ReturnsRedirectToRoot metodą, baza danych jest ponownie zainicjowana w tej metodzie testowej, aby upewnić się, że rekord jest obecny, aby SUT mógł usunąć. Wybranie pierwszego przycisku usuwania w formularzu messages w SUT jest symulowane w żądaniu do SUT:
[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);
}
Opcje klienta
Zobacz stronę WebApplicationFactoryClientOptions dotyczącą ustawień domyślnych i dostępnych opcji podczas tworzenia instancji HttpClient.
Utwórz klasę WebApplicationFactoryClientOptions i przekaż ją do CreateClient() metody :
UWAGA: Aby uniknąć ostrzeżeń przekierowania HTTPS w dziennikach podczas korzystania z oprogramowania pośredniczącego przekierowania HTTPS, ustaw BaseAddress = new Uri("https://localhost")
Wstrzyknij usługi testowe
Usługi można zastąpić w teście za pomocą wywołania metody ConfigureTestServices w konstruktorze hosta. Aby ograniczyć zakres nadpisanych usług do samego testu, metoda WithWebHostBuilder jest używana w celu uzyskania konstruktora hosta. Można to zobaczyć w następujących testach:
Przykładowy SUT zawiera usługę o zdefiniowanym zakresie, która zwraca cytat. Cytat jest osadzony w ukrytym polu na stronie indeks, gdy żądana jest strona indeks.
Services/IQuoteService.cs:
public interface IQuoteService
{
Task<string> GenerateQuote();
}
Po uruchomieniu aplikacji SUT jest generowany następujący kod:
<input id="quote" type="hidden" value="Come on, Sarah. We've an appointment in
London, and we're already 30,000 years late.">
Aby przetestować usługę i przeprowadzić iniekcję w teście integracji, usługa symulowana jest wstrzykiwana do systemu testowanego przez test. Usługa pozorowania zastępuje aplikację QuoteService usługą dostarczaną przez aplikację testową o nazwie TestQuoteService:
ConfigureTestServices zostaje wywołana, a usługa o określonym zakresie zostaje zarejestrowana:
[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);
}
Znacznik wygenerowany podczas wykonywania testu odzwierciedla tekst cudzysłowu dostarczony przez element TestQuoteService, w związku z czym asercja przechodzi.
<input id="quote" type="hidden" value="Something's interfering with time,
Mr. Scarman, and time is my business.">
Symulacja uwierzytelniania
Testy w AuthTests klasie sprawdzają, czy bezpieczny punkt końcowy:
Przekierowuje nieuwierzytelnionego użytkownika do strony logowania aplikacji.
Zwraca zawartość dla uwierzytelnionego użytkownika.
Na stronie /SecurePage w SUT używana jest konwencja AuthorizePage do zastosowania elementu AuthorizeFilter na stronie. Aby uzyskać więcej informacji, zobacz Razor Konwencje autoryzacji stron.
[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);
}
Nie zezwalając klientowi na obserwowanie przekierowania, można wykonać następujące kontrole:
Kod stanu zwrócony przez SUT można sprawdzić względem oczekiwanego HttpStatusCode.Redirect wyniku, a nie końcowego kodu stanu po przekierowaniu do strony logowania, który byłby HttpStatusCode.OK.
Wartość nagłówka Location w nagłówkach odpowiedzi jest sprawdzana, aby potwierdzić, że rozpoczyna się od http://localhost/Identity/Account/Login, a nie od odpowiedzi końcowej strony logowania, gdzie nagłówek Location nie byłby obecny.
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);
}
}
Element TestAuthHandler jest wywoływany w celu uwierzytelnienia użytkownika, gdy ustawiono schemat uwierzytelniania jako TestScheme, gdzie AddAuthentication jest zarejestrowany dla ConfigureTestServices. Ważne jest, TestScheme aby schemat był zgodny ze schematem oczekiwanym przez aplikację. W przeciwnym razie uwierzytelnianie nie będzie działać.
Zobacz to repozytorium GitHub, aby zapoznać się z podstawowymi testami oprogramowania pośredniczącego uwierzytelniania. Zawiera on serwer testowy specyficzny dla scenariusza testowego.
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");
}
}
Jak infrastruktura testowa wywnioskuje ścieżkę główną zawartości aplikacji
Konstruktor WebApplicationFactory ustala ścieżkę katalogu głównego zawartości aplikacji poprzez wyszukiwanie elementu WebApplicationFactoryContentRootAttribute w zestawie, który zawiera testy integracyjne, z kluczem równym zestawowi TEntryPointSystem.Reflection.Assembly.FullName. Jeśli atrybut z poprawnym kluczem nie zostanie znaleziony, WebApplicationFactory wraca do wyszukiwania pliku rozwiązania (.sln) i dodaje TEntryPoint nazwę zestawu do katalogu rozwiązania. Katalog główny aplikacji (ścieżka główna zawartości) służy do odnajdywania widoków i plików zawartości.
Wyłącz kopiowanie w tle
Kopiowanie w tle powoduje wykonanie testów w innym katalogu niż katalog wyjściowy. Jeśli testy polegają na ładowaniu plików względem Assembly.Location i napotkasz problemy, może być konieczne wyłączenie kopiowania w tle.
Aby wyłączyć kopiowanie cieni przy użyciu xUnit, utwórz plik xunit.runner.json w katalogu projektu testowego z poprawnym ustawieniem konfiguracji:
{
"shadowCopy": false
}
Usuwanie obiektów
Po przeprowadzeniu testów implementacji IClassFixture, TestServer i HttpClient są usuwane, gdy xUnit usuwa WebApplicationFactory. Jeśli obiekty utworzone przez dewelopera wymagają usunięcia, należy je usunąć w implementacji IClassFixture . Aby uzyskać więcej informacji, zobacz Metoda Dispose — implementacja.
Przykład testów integracji
Przykładowa aplikacja składa się z dwóch aplikacji:
Aplikacja
Katalog projektu
opis
Aplikacja komunikatów (SUT)
src/RazorPagesProject
Umożliwia użytkownikowi dodawanie, usuwanie jednego, usuwanie wszystkich i analizowanie komunikatów.
Testowanie aplikacji
tests/RazorPagesProject.Tests
Służy do testowania integracji systemu (SUT).
Testy można uruchamiać przy użyciu wbudowanych funkcji testowych środowiska IDE, takich jak Visual Studio. Jeśli używasz programu Visual Studio Code lub wiersza polecenia, wykonaj następujące polecenie w wierszu polecenia w tests/RazorPagesProject.Tests katalogu:
dotnet test
Organizacja aplikacji wiadomości (SUT)
SUT to Razor system komunikatów Pages o następujących cechach:
Strona Indeks aplikacji (Pages/Index.cshtml i Pages/Index.cshtml.cs) udostępnia metody interfejsu użytkownika i modelu strony do kontrolowania dodawania, usuwania i analizowania komunikatów (średnie słowa na komunikat).
Komunikat jest opisany przez klasę Message (Data/Message.cs) z dwiema właściwościami: Id (klucz) i Text (komunikat). Właściwość Text jest wymagana i ograniczona do 200 znaków.
Komunikaty są przechowywane przy użyciu bazy danych w pamięci programu Entity Framework†.
Aplikacja zawiera warstwę dostępu do danych (DAL) w swojej klasie AppDbContext kontekstu bazy danych (Data/AppDbContext.cs).
Jeśli baza danych jest pusta podczas uruchamiania aplikacji, magazyn komunikatów jest inicjowany z trzema komunikatami.
Aplikacja zawiera element /SecurePage , do którego można uzyskać dostęp tylko przez uwierzytelnionego użytkownika.
† Artykuł EF, Test z InMemory, wyjaśnia, jak używać bazy danych w pamięci do testów przy użyciu narzędzia MSTest. W tym temacie jest używana struktura testowa xUnit . Koncepcje testów i implementacje testów w różnych platformach testowych są podobne, ale nie identyczne.
Aplikacja testowa jest aplikacją konsolową wewnątrz tests/RazorPagesProject.Tests katalogu.
Testowanie katalogu aplikacji
opis
AuthTests
Zawiera metody testowe dla:
Uzyskiwanie dostępu do bezpiecznej strony przez nieuwierzytelnionego użytkownika.
Uzyskiwanie dostępu do bezpiecznej strony przez uwierzytelnionego użytkownika z użyciem pozornego elementu AuthenticationHandler<TOptions>.
Uzyskiwanie profilu użytkownika usługi GitHub i sprawdzanie logowania użytkownika profilu.
BasicTests
Zawiera metodę testową routingu i typu zawartości.
IntegrationTests
Zawiera testy integracji dla strony Indeks przy użyciu klasy niestandardowej WebApplicationFactory .
Helpers/Utilities
Utilities.cs zawiera metodę InitializeDbForTests, używaną do zasiewania bazy danych danymi testowymi.
HtmlHelpers.cs Udostępnia metodę zwracania obiektu AngleSharp IHtmlDocument do użytku przez metody testowe.
HttpClientExtensions.cs zapewnij przeciążenia dla SendAsync aby przesyłać żądania do SUT.
Framework testowy to xUnit. Testy integracji są przeprowadzane przy użyciu Microsoft.AspNetCore.TestHost, który zawiera TestServer. Ponieważ pakiet Microsoft.AspNetCore.Mvc.Testing jest używany do konfigurowania hosta testowego i serwera testowego, pakiety TestHost i TestServer nie wymagają bezpośrednich odwołań w pliku projektu aplikacji testowej ani ręcznej konfiguracji przez dewelopera w tej aplikacji.
Testy integracji zwykle wymagają małego zestawu danych w bazie danych przed wykonaniem testu. Na przykład test usuwania wywołuje usunięcie rekordu bazy danych, więc baza danych musi mieć co najmniej jeden rekord, aby żądanie usunięcia powiodło się.
Przykładowa aplikacja zapełnia bazę danych trzema komunikatami w Utilities.cs, które mogą być używane przez testy podczas ich wykonywania:
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." }
};
}
Kontekst bazy danych SUT jest zarejestrowany w pliku Program.cs. Wywołanie zwrotne aplikacji builder.ConfigureServices testowej jest wykonywane po wykonaniu kodu aplikacji Program.cs . Aby użyć innej bazy danych do testów, kontekst bazy danych aplikacji musi zostać zastąpiony w pliku builder.ConfigureServices. Aby uzyskać więcej informacji, zobacz sekcję Customize WebApplicationFactory.
Do testowania spAs zalecamy narzędzie, takie jak Playwright dla platformy .NET, które może zautomatyzować przeglądarkę.
Wprowadzenie do testów integracji
Testy integracji oceniają składniki aplikacji na szerszym poziomie niż testy jednostkowe. Testy jednostkowe służą do testowania izolowanych składników oprogramowania, takich jak poszczególne metody klasy. Testy integracji potwierdzają, że co najmniej dwa składniki aplikacji współpracują ze sobą, aby wygenerować oczekiwany wynik, prawdopodobnie włącznie z każdym składnikiem wymaganym do pełnego przetworzenia żądania.
Te szersze testy służą do testowania infrastruktury aplikacji i całej struktury, często w tym następujących składników:
baza danych
System plików
Urządzenia sieciowe
Przepływ żądań i odpowiedzi
Testy jednostkowe używają sfabrykowanych składników, znanych jako fałszywe lub makiety obiektów, zamiast składników infrastruktury.
W przeciwieństwie do testów jednostkowych, testy integracji:
Użyj rzeczywistych składników używanych przez aplikację w środowisku produkcyjnym.
Wymagaj więcej kodu i przetwarzania danych.
Uruchamianie trwa dłużej.
W związku z tym ogranicz użycie testów integracji do najważniejszych scenariuszy infrastruktury. Jeśli zachowanie można przetestować przy użyciu testu jednostkowego lub testu integracji, wybierz test jednostkowy.
W dyskusjach na temat testów integracyjnych testowany projekt jest często nazywany Systemem Pod Testami, lub w skrócie "SUT". Element "SUT" jest używany w tym artykule do odwoływania się do testowanej aplikacji ASP.NET Core.
Nie zapisuj testów integracji dla każdej permutacji danych i dostępu do plików za pomocą baz danych i systemów plików. Niezależnie od liczby miejsc w aplikacji współdziała z bazami danych i systemami plików, skoncentrowany zestaw testów integracji odczytu, zapisu, aktualizacji i usuwania zwykle umożliwia odpowiednie testowanie składników bazy danych i systemu plików. Używaj testów jednostkowych do rutynowych testów logiki metod, które wchodzą w interakcje z tymi składnikami. W testach jednostkowych użycie fałszywych elementów infrastruktury lub imitacji przyspiesza wykonywanie testów.
testy integracji ASP.NET Core
Testy integracji w programie ASP.NET Core wymagają następujących elementów:
Projekt testowy służy do zawierania i wykonywania testów. Projekt testowy zawiera odwołanie do SUT.
Projekt testowy tworzy serwer testowy dla SUT i korzysta z klienta serwera testowego do obsługi żądań i odpowiedzi z SUT.
Moduł uruchamiający testy służy do wykonywania testów i zgłaszania wyników testu.
Testy integracji są zgodne z sekwencją zdarzeń obejmującą zwykłe kroki testowe: Rozmieszczenie, Działanie i Sprawdzenie.
Host internetowy SUT jest skonfigurowany.
Klient serwera testowego jest tworzony w celu przesyłania żądań do aplikacji.
Etap Aranżacji testu jest realizowany: aplikacja testowa przygotowuje żądanie.
Krok Act testu jest wykonywany: klient przesyła żądanie i odbiera odpowiedź.
Krok testu potwierdzenia jest wykonywany: rzeczywista odpowiedź jest weryfikowana jako powodzenie lub niepowodzenie w oparciu o oczekiwaną odpowiedź.
Proces będzie kontynuowany do momentu wykonania wszystkich testów.
Wyniki testów są zgłaszane.
Zazwyczaj testowy host internetowy jest skonfigurowany inaczej niż normalny host internetowy aplikacji na potrzeby przebiegów testu. Na przykład do testów mogą być używane różne ustawienia bazy danych lub różnych aplikacji.
Składniki infrastruktury, takie jak testowy host internetowy i serwer testowy w pamięci (TestServer), są dostarczane lub zarządzane przez pakiet Microsoft.AspNetCore.Mvc.Testing . Użycie tego pakietu usprawnia tworzenie i wykonywanie testów.
Kopiuje plik zależności (.deps) z sutu do katalogu projektu testowego bin .
Ustawia katalog główny zawartości na katalog główny projektu SUT, tak aby pliki statyczne i strony/widoki zostały znalezione podczas wykonywania testów.
Udostępnia klasę WebApplicationFactory , aby usprawnić uruchamianie aplikacji SUT za pomocą polecenia TestServer.
W dokumentacji testów jednostkowych opisano sposób konfigurowania projektu testowego i modułu uruchamiającego testy oraz szczegółowe instrukcje dotyczące uruchamiania testów i zaleceń dotyczących sposobu nazywania testów i klas testowych.
Oddziel testy jednostkowe od testów integracji do różnych projektów. Oddzielanie testów:
Pomaga zagwarantować, że składniki testowania infrastruktury nie zostaną przypadkowo uwzględnione w testach jednostkowych.
Umożliwia kontrolę nad tym, który zestaw testów jest uruchamiany.
Praktycznie nie ma różnicy między konfiguracją testów Razor aplikacji Pages i aplikacji MVC. Jedyną różnicą jest sposób, w jaki testy są nazwane.
Razor W aplikacji Pages testy punktów końcowych strony są zwykle nazwane po klasie modelu strony (na przykład IndexPageTests w celu przetestowania integracji składników dla strony Indeks). W aplikacji MVC testy są zwykle zorganizowane według klas kontrolerów i nazwane po testowych kontrolerach (na przykład HomeControllerTests do testowania integracji składników dla Home kontrolera).
Określ zestaw Web SDK w pliku projektu (<Project Sdk="Microsoft.NET.Sdk.Web">).
Te wymagania wstępne można zobaczyć w przykładowej aplikacji.
tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj Sprawdź plik. Przykładowa aplikacja używa struktury testowej xUnit i biblioteki analizatora AngleSharp , więc przykładowa aplikacja również odwołuje się do:
Klasy testowe implementują interfejs fixture klasy (IClassFixture), aby wskazać, że klasa zawiera testy i udostępnia wspólne wystąpienia obiektów między testami w klasie.
Następująca klasa testowa BasicTests używa WebApplicationFactory do uruchomienia SUT i dostarczenia HttpClient do metody testowej Get_EndpointsReturnSuccessAndCorrectContentType. Metoda sprawdza, czy kod stanu odpowiedzi jest pomyślny (200–299), a nagłówek ma wartość Content-Type na kilku stronach aplikacji.
CreateClient() Program tworzy wystąpienie HttpClient , które automatycznie śledzi przekierowania i obsługuje pliki cookie.
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());
}
}
Domyślnie pliki cookie inne niż podstawowe nie są zachowywane w żądaniach, gdy zasady zgody ogólnego rozporządzenia o ochronie danych są włączone. Aby zachować nieistotne pliki cookie, takie jak te używane przez dostawcę TempData, oznacz je jako niezbędne w testach. Aby uzyskać instrukcje dotyczące oznaczania cookie elementu jako niezbędnego, zobacz Podstawowe pliki cookie.
AngleSharp vs Application Parts kontra antyfałszerstwo
W tym artykule użyto analizatora AngleSharp do obsługi testów antyforgeryjnych przez ładowanie stron i analizowanie kodu HTML. Aby przetestować punkty końcowe kontrolera i widoki stron Razor na niższym poziomie, bez martwienia się o sposób renderowania w przeglądarce, rozważ użycie Application Parts. Metoda Części aplikacji wprowadza kontroler lub Razor stronę do aplikacji, która może służyć do tworzenia żądań JSON w celu uzyskania wymaganych wartości. Aby uzyskać więcej informacji, zobacz blog Testowanie integracji zasobów ASP.NET Core chronionych przed fałszerstwami przy użyciu części aplikacji oraz powiązane repozytorium GitHub autorstwa Martina Costello.
Dostosować WebApplicationFactory
Konfigurację hosta internetowego można utworzyć niezależnie od klas testowych, dziedzicząc z WebApplicationFactory<TEntryPoint> w celu utworzenia jednej lub więcej niestandardowych fabryk.
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");
}
}
Kontekst bazy danych SUT jest zarejestrowany w pliku Program.cs. Wywołanie zwrotne aplikacji builder.ConfigureServices testowej jest wykonywane po wykonaniu kodu aplikacji Program.cs . Aby użyć innej bazy danych do testów niż baza danych aplikacji, kontekst bazy danych aplikacji musi zostać zastąpiony w pliku builder.ConfigureServices.
Przykładowa aplikacja znajduje deskryptor usługi dla kontekstu bazy danych i używa deskryptora do usunięcia rejestracji usługi. Następnie fabryka dodaje nowy ApplicationDbContext , który używa bazy danych w pamięci do testów.
Aby nawiązać połączenie z inną bazą danych, zmień wartość DbConnection. Aby użyć testowej bazy danych programu SQL Server:
Dodaj odwołanie do pakietu NuGet w pliku projektu.
Zadzwoń UseInMemoryDatabase.
Użyj niestandardowego CustomWebApplicationFactory w klasach testowych. W poniższym przykładzie użyto fabryki w klasie IndexPageTests.
Klient przykładowej aplikacji jest skonfigurowany tak, aby zapobiec HttpClient następującym przekierowaniom. Jak wyjaśniono w sekcji Mock uwierzytelnianie, pozwala to testom na sprawdzenie wyniku pierwszej odpowiedzi aplikacji. Pierwsza odpowiedź w wielu z tych testów to przekierowanie z nagłówkiem Location.
Typowy test używa HttpClient oraz metod pomocniczych do przetwarzania żądania i odpowiedzi.
[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);
}
Każde żądanie POST do SUT musi spełniać kontrolę antyforgeryjną, która jest automatycznie dokonana przez system ochrony danych aplikacji. Aby zorganizować żądanie POST testu, aplikacja testowa musi:
Utwórz żądanie dla strony.
Przeanalizuj token antyforgery i cookie żądaj weryfikacji z odpowiedzi.
Zrób żądanie POST z tokenem antyforgery cookie oraz tokenem weryfikacji żądania.
SendAsync Pomocnicze metody rozszerzenia (Helpers/HttpClientExtensions.cs) i metoda pomocnika (GetDocumentAsync) w przykładowej Helpers/HtmlHelpers.cs używają analizatora AngleSharp do obsługi sprawdzania pod kątem ochrony przed fałszerstwami za pomocą następujących metod:
GetDocumentAsync: Odbiera element HttpResponseMessage i zwraca wartość IHtmlDocument.
GetDocumentAsync używa mechanizmu, który przygotowuje wirtualną odpowiedź na podstawie oryginalnego HttpResponseMessage. Aby uzyskać więcej informacji, zobacz dokumentację AngleSharp.
SendAsync metody rozszerzenia dla HttpClient tworzą HttpRequestMessage i wywołują SendAsync(HttpRequestMessage) w celu przesłania żądań do SUT. Przeciążenia dla SendAsync akceptują formularz HTML (IHtmlFormElement) i następujące elementy:
Przycisk Prześlij formularza (IHtmlElement)
Kolekcja wartości formularza (IEnumerable<KeyValuePair<string, string>>)
Przycisk Prześlij (IHtmlElement) i wartości formularza (IEnumerable<KeyValuePair<string, string>>)
AngleSharp to biblioteka do analizy od strony trzeciej używana do celów demonstracyjnych w tym artykule i przykładowej aplikacji. Funkcja AngleSharp nie jest obsługiwana ani wymagana do testowania integracji aplikacji ASP.NET Core. Można użyć innych analizatorów, takich jak Pakiet Zwinności HTML (HAP). Innym podejściem jest napisanie kodu do obsługi tokenu weryfikacji żądania systemu ochrony przed fałszerstwem oraz bezpośredniego zarządzania systemem antyfałszerstwowym cookie. Aby uzyskać więcej informacji, zobacz AngleSharp kontra Application Parts do weryfikacji antyfałszerstw w tym artykule.
Dostawca bazy danych EF-Core w pamięci może służyć do ograniczonego i podstawowego testowania, jednak dostawca SQLite jest zalecanym wyborem do testowania w pamięci.
Dostosowywanie klienta za pomocą programu WithWebHostBuilder
Jeśli w metodzie testowej wymagana jest dodatkowa konfiguracja, WithWebHostBuilder tworzy nowy WebApplicationFactory, zawierający IWebHostBuilder, który został dalej skonfigurowany zgodnie z potrzebami.
Przykładowy kod wywołuje aby zastąpić skonfigurowane usługi próbkami testowymi. Aby uzyskać więcej informacji i przykłady użycia, zapoznaj się z sekcją Wstrzykiwanie usług mock w tym artykule.
Metoda Post_DeleteMessageHandler_ReturnsRedirectToRoot testowa przykładowej aplikacji demonstruje użycie metody WithWebHostBuilder. Ten test wykonuje usunięcie rekordu w bazie danych, wyzwalając przesłanie formularza w SUT.
Ponieważ inny test w IndexPageTests klasie wykonuje operację, która usuwa wszystkie rekordy w bazie danych i może zostać uruchomiona przed Post_DeleteMessageHandler_ReturnsRedirectToRoot metodą, baza danych jest ponownie zainicjowana w tej metodzie testowej, aby upewnić się, że rekord jest obecny, aby SUT mógł usunąć. Wybranie pierwszego przycisku usuwania w formularzu messages w SUT jest symulowane w żądaniu do SUT:
[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);
}
Opcje klienta
Zobacz stronę WebApplicationFactoryClientOptions dotyczącą ustawień domyślnych i dostępnych opcji podczas tworzenia instancji HttpClient.
Utwórz klasę WebApplicationFactoryClientOptions i przekaż ją do CreateClient() metody :
UWAGA: Aby uniknąć ostrzeżeń przekierowania HTTPS w dziennikach podczas korzystania z oprogramowania pośredniczącego dla przekierowania HTTPS, ustaw BaseAddress = new Uri("https://localhost")
Wstrzykiwanie usług testowych
Usługi można zastąpić w teście za pomocą wywołania funkcji ConfigureTestServices w konstruktorze hosta. Aby ograniczyć zakres nadpisanych usług do samego testu, metoda WithWebHostBuilder jest używana w celu uzyskania konstruktora hosta. Można to zobaczyć w następujących testach:
Przykładowy SUT zawiera usługę o zdefiniowanym zakresie, która zwraca cytat. Cytat jest osadzony w ukrytym polu na stronie indeks, gdy żądana jest strona indeks.
Services/IQuoteService.cs:
public interface IQuoteService
{
Task<string> GenerateQuote();
}
Po uruchomieniu aplikacji SUT jest generowany następujący kod:
<input id="quote" type="hidden" value="Come on, Sarah. We've an appointment in
London, and we're already 30,000 years late.">
Aby przetestować usługę i przeprowadzić iniekcję w teście integracji, usługa symulowana jest wstrzykiwana do systemu testowanego przez test. Usługa pozorowania zastępuje aplikację QuoteService usługą dostarczaną przez aplikację testową o nazwie TestQuoteService:
ConfigureTestServices zostaje wywołana, a usługa o określonym zakresie zostaje zarejestrowana:
[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);
}
Znacznik wygenerowany podczas wykonywania testu odzwierciedla tekst cudzysłowu dostarczony przez element TestQuoteService, w związku z czym asercja przechodzi.
<input id="quote" type="hidden" value="Something's interfering with time,
Mr. Scarman, and time is my business.">
Symulacja uwierzytelniania
Testy w klasie AuthTests sprawdzają, czy punkt końcowy jest bezpieczny.
Przekierowuje nieuwierzytelnionego użytkownika do strony logowania aplikacji.
Zwraca zawartość dla uwierzytelnionego użytkownika.
Na stronie SUT /SecurePage używana jest konwencja AuthorizePage do zastosowania elementu AuthorizeFilter na stronie. Aby uzyskać więcej informacji, zobacz Razor Konwencje autoryzacji stron.
[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);
}
Nie zezwalając klientowi na obserwowanie przekierowania, można wykonać następujące kontrole:
Kod stanu zwrócony przez SUT można sprawdzić względem oczekiwanego HttpStatusCode.Redirect wyniku, a nie końcowego kodu stanu po przekierowaniu do strony logowania, który byłby HttpStatusCode.OK.
Wartość nagłówka Location w nagłówkach odpowiedzi jest sprawdzana, aby potwierdzić, że rozpoczyna się od http://localhost/Identity/Account/Login, a nie od odpowiedzi końcowej strony logowania, gdzie nagłówek Location nie byłby obecny.
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);
}
}
Element TestAuthHandler jest wywoływany w celu uwierzytelnienia użytkownika, gdy ustawiono schemat uwierzytelniania jako TestScheme, gdzie AddAuthentication jest zarejestrowany dla ConfigureTestServices. Ważne jest, TestScheme aby schemat był zgodny ze schematem oczekiwanym przez aplikację. W przeciwnym razie uwierzytelnianie nie będzie działać.
Zobacz to repozytorium GitHub, aby zapoznać się z podstawowymi testami oprogramowania pośredniczącego uwierzytelniania. Zawiera on serwer testowy specyficzny dla scenariusza testowego.
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");
}
}
Jak infrastruktura testowa wywnioskuje ścieżkę główną zawartości aplikacji
Konstruktor WebApplicationFactory ustala ścieżkę katalogu głównego zawartości aplikacji poprzez wyszukiwanie elementu WebApplicationFactoryContentRootAttribute w zestawie, który zawiera testy integracyjne, z kluczem równym zestawowi TEntryPointSystem.Reflection.Assembly.FullName. Jeśli atrybut z poprawnym kluczem nie zostanie znaleziony, WebApplicationFactory wraca do wyszukiwania pliku rozwiązania (.sln) i dodaje TEntryPoint nazwę zestawu do katalogu rozwiązania. Katalog główny aplikacji (ścieżka główna zawartości) służy do odnajdywania widoków i plików zawartości.
Wyłącz kopiowanie w tle
Kopiowanie w tle powoduje wykonanie testów w innym katalogu niż katalog wyjściowy. Jeśli testy polegają na ładowaniu plików względem Assembly.Location i napotkasz problemy, może być konieczne wyłączenie kopiowania w tle.
Aby wyłączyć kopiowanie cieni przy użyciu xUnit, utwórz plik xunit.runner.json w katalogu projektu testowego z poprawnym ustawieniem konfiguracji:
{
"shadowCopy": false
}
Usuwanie obiektów
Po przeprowadzeniu testów implementacji IClassFixture, TestServer i HttpClient są usuwane, gdy xUnit usuwa WebApplicationFactory. Jeśli obiekty utworzone przez dewelopera wymagają usunięcia, należy je usunąć w implementacji IClassFixture . Aby uzyskać więcej informacji, zobacz Metoda Dispose — implementacja.
Przykład testów integracji
Przykładowa aplikacja składa się z dwóch aplikacji:
Aplikacja
Katalog projektu
opis
Aplikacja komunikatów (SUT)
src/RazorPagesProject
Umożliwia użytkownikowi dodawanie, usuwanie jednego, usuwanie wszystkich i analizowanie komunikatów.
Testowanie aplikacji
tests/RazorPagesProject.Tests
Służy do testowania integracji systemu (SUT).
Testy można uruchamiać przy użyciu wbudowanych funkcji testowych środowiska IDE, takich jak Visual Studio. Jeśli używasz programu Visual Studio Code lub wiersza polecenia, wykonaj następujące polecenie w wierszu polecenia w tests/RazorPagesProject.Tests katalogu:
dotnet test
Organizacja aplikacji wiadomości (SUT)
SUT to Razor system komunikatów Pages o następujących cechach:
Strona Indeks aplikacji (Pages/Index.cshtml i Pages/Index.cshtml.cs) udostępnia metody interfejsu użytkownika i modelu strony do kontrolowania dodawania, usuwania i analizowania komunikatów (średnie słowa na komunikat).
Komunikat jest opisany przez klasę Message (Data/Message.cs) z dwiema właściwościami: Id (klucz) i Text (komunikat). Właściwość Text jest wymagana i ograniczona do 200 znaków.
Komunikaty są przechowywane przy użyciu bazy danych w pamięci programu Entity Framework†.
Aplikacja zawiera warstwę dostępu do danych (DAL) w swojej klasie AppDbContext kontekstu bazy danych (Data/AppDbContext.cs).
Jeśli baza danych jest pusta podczas uruchamiania aplikacji, magazyn komunikatów jest inicjowany z trzema komunikatami.
Aplikacja zawiera element /SecurePage , do którego można uzyskać dostęp tylko przez uwierzytelnionego użytkownika.
† Artykuł EF, Test z InMemory, wyjaśnia, jak używać bazy danych w pamięci do testów przy użyciu narzędzia MSTest. W tym temacie jest używana struktura testowa xUnit . Koncepcje testów i implementacje testów w różnych platformach testowych są podobne, ale nie identyczne.
Aplikacja testowa jest aplikacją konsolową wewnątrz tests/RazorPagesProject.Tests katalogu.
Testowanie katalogu aplikacji
opis
AuthTests
Zawiera metody testowe dla:
Uzyskiwanie dostępu do bezpiecznej strony przez nieuwierzytelnionego użytkownika.
Uzyskiwanie dostępu do bezpiecznej strony przez uwierzytelnionego użytkownika z użyciem pozornego elementu AuthenticationHandler<TOptions>.
Uzyskiwanie profilu użytkownika usługi GitHub i sprawdzanie logowania użytkownika profilu.
BasicTests
Zawiera metodę testową routingu i typu zawartości.
IntegrationTests
Zawiera testy integracji dla strony Indeks przy użyciu klasy niestandardowej WebApplicationFactory .
Helpers/Utilities
Utilities.cs zawiera metodę InitializeDbForTests, używaną do zasiewania bazy danych danymi testowymi.
HtmlHelpers.cs Udostępnia metodę zwracania obiektu AngleSharp IHtmlDocument do użytku przez metody testowe.
HttpClientExtensions.cs zapewnij przeciążenia dla SendAsync aby przesyłać żądania do SUT.
Struktura testowa to xUnit. Testy integracji są przeprowadzane przy użyciu Microsoft.AspNetCore.TestHost, który zawiera TestServer. Ponieważ pakiet Microsoft.AspNetCore.Mvc.Testing jest używany do konfigurowania hosta testowego i serwera testowego, pakiety TestHost i TestServer nie wymagają bezpośrednich odwołań w pliku projektu aplikacji testowej ani ręcznej konfiguracji przez dewelopera w tej aplikacji.
Testy integracji zwykle wymagają małego zestawu danych w bazie danych przed wykonaniem testu. Na przykład test usuwania wywołuje usunięcie rekordu bazy danych, więc baza danych musi mieć co najmniej jeden rekord, aby żądanie usunięcia powiodło się.
Przykładowa aplikacja zapełnia bazę danych trzema komunikatami w Utilities.cs, które mogą być używane przez testy podczas ich wykonywania:
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." }
};
}
Kontekst bazy danych SUT jest zarejestrowany w pliku Program.cs. Wywołanie zwrotne aplikacji testowej builder.ConfigureServices jest realizowane po wykonaniu kodu tej aplikacji Program.cs. Aby użyć innej bazy danych do testów, kontekst bazy danych aplikacji musi zostać zastąpiony w pliku builder.ConfigureServices. Aby uzyskać więcej informacji, zobacz sekcję Customize WebApplicationFactory.
Źródło tej zawartości można znaleźć w witrynie GitHub, gdzie można również tworzyć i przeglądać problemy i żądania ściągnięcia. Więcej informacji znajdziesz w naszym przewodniku dla współtwórców.
Opinia o produkcie ASP.NET Core
ASP.NET Core to projekt typu open source. Wybierz link, aby przekazać opinię:
Rozpocznij testowanie aplikacji języka C# przy użyciu narzędzi do testowania w programie Visual Studio. Dowiedz się, jak pisać testy, używać Eksploratora testów, tworzyć zestawy testów i stosować czerwony, zielony, refaktoryzowy wzorzec do pisania kodu.
Architektura mikrousług platformy .NET dla konteneryzowanych aplikacji .NET | Zapoznaj się z architekturą do testowania usług ASP.NET Core i aplikacji internetowych w kontenerach.