Перевод статьи «Play with Playwright».
Playwright — это фреймворк для тестирования веб-интерфейса от компании Microsoft, в котором можно работать с различными языками (поддерживает JavaScript/TypeScript, Python, Java и, конечно же, C#). Он используется с фреймворками для модульного тестирования, и поэтому его можно запустить в рамках конвейера CI/CD. Синтаксис интуитивно понятен, и мне он очень нравится. Кроме того, документация очень хороша и помогает легко начать работу.
В этой статье я не буду знакомить вас с Playwright. Официальный сайт и документация – гораздо лучший ресурс для знакомства. Я бы хотел поиграться с ним и использовать его по-другому. Вместо того чтобы тестировать уже загруженное на хост веб-приложение, я бы хотел протестировать веб-приложение, которое размещается в тестовом проекте, с помощью фабрики WebApplicationFactory. Таким образом можно получить действительно изолированные UI-тесты, не связанные с другой инфраструктурой и не падающие из-за проблем с сетью.
Сработает ли это? Давайте попробуем!
Друзья, поддержите нас вступлением в наш телеграм канал QaRocks. Там много туториалов, задач по автоматизации и книг по QA.
Настройка решения
Следующие строки создают проект ASP.NET Core MVC и проект тестов NUnit. После этого будет создан файл решения, и названные проекты будут добавлены в него. Последняя команда добавляет реализацию NUnit Playwright в тестовый проект:
dotnet new mvc -n PlayWithPlaywright dotnet new nunit -n PlayWithPlaywright.Tests dotnet new sln -n PLayWithPlaywright dotnet sln add .\PlayWithPlaywright\ dotnet sln add .\PlayWithPlaywright.Tests\ dotnet add .\PlayWithPlaywright.Tests\ package Microsoft.Playwright.NUnit
Выполните эти команды и сбилдите решение:
dotnet build
Билд нужен для копирования скрипта PowerShell в выходной каталог тестового проекта. Этот скрипт PowerShell — интерфейс командной строки для управления Playwright.
Далее нам нужно установить необходимые браузеры для выполнения тестов через PowerShell:
.\PlayWithPlaywright.Tests\bin\Debug\net7.0\playwright.ps1 install
Генерация тестового кода
Команда codegen позволяет автоматически сгенерировать тестовый код, который затем можно скопировать в тестовый проект:
.\PlayWithPlaywright.Tests\bin\Debug\net7.0\playwright.ps1 codegen https://asp.net-hacker.rocks/
Эта команда открывает Playwright Inspector, в котором вы можете записать тестовый сценарий. Во время перехода по приложению справа будет генерироваться тестовый код:

Вместо того чтобы тестировать внешний сайт, как это делал я, вы можете вызвать codegen на локально запущенном приложении.
Просто скопируйте сгенерированный код в тестовый проект NUnit и исправьте пространство имен и имя класса так, чтобы они соответствовали пространству имен вашего проекта.
Используя сгенерированный код в качестве примера, вы сможете написать больше тестов вручную.
Когда все будет готово, запустите dotnet test, чтобы выполнить сгенерированный тест и убедиться, что Playwright работает.
Начинаем играться
Обычно Playwright тестирует приложения, запущенные на сервере. При этом возникает одна простая проблема: если тест не может подключиться к запущенному приложению из-за проблем с сетью, он проваливается. А между тем, тест должен иметь только одну причину для «фейла» — некорректную работу проверяемой программы.
Решением может стать тестирование веб-приложения, размещенного на той же инфраструктуре, где расположен тест, и в рамках того же процесса.
Microsoft уже предоставила возможность писать интеграционные тесты для веб-приложений с помощью WebApplicationFactory. Моя идея заключалась в том, чтобы использовать WebApplicationFactory для хостинга приложения, которое можно тестировать с помощью Playwright.
Поскольку WebApplicationFactory также предоставляет HttpClient, я ожидал бы получить URL для подключения. Этот HttpClient будет иметь BaseAddress, который я могу использовать для передачи в Playwright.
WebApplicationFactory и Playwright
По умолчанию мы не можем объединить WebApplicationFactory и Playwright, потому что WebApplicationFactory на самом деле не «хостит» веб-приложение по HTTP. Это означает, что здесь не используется Kestrel для открытия конечной точки по HTTP. WebApplicationFactory создает тестовый сервер, который размещает приложение в памяти и просто имитирует настоящий HTTP-сервер.
Нам нужно найти способ запустить HTTP-сервер, такой как Kestrel, чтобы разместить приложение. На самом деле мы могли бы запустить WebApplicationBuilder, но идея заключалась в том, чтобы повторно использовать конфигурацию Program.cs приложения, которое мы хотим протестировать (как это делается с помощью WebApplicationFactory).
Дэниел Донбаванд нашел решение, как переопределить WebApplicationFactory для реального размещения приложения по HTTP и получить конечную точку, которая может быть использована с Playwright. Я использовал решение Дэниелса, но сделал его более универсальным.
Давайте посмотрим, как это работает вместе с Playwright.
Сначала добавьте ссылку на веб-проект в тестовом проекте Playwright и на пакет в Microsoft.AspNetCore.Mvc.Testing.
dotnet add .\PlayWithPlaywright.Tests\ reference .\PlayWithPlaywright\ dotnet add .\PlayWithPlaywright.Tests\ package Microsoft.AspNetCore.Mvc.Testing
Первая необходима для использования Program.cs с WebApplicationFactory. Вторая добавляет WebApplicationFactory и тестовый сервер в тестовый проект.
Чтобы использовать класс Program, определенный в Program.cs, который использует минимальный API, вы можете добавить пустой частичный класс Program в Program.cs.
Я просто поместил следующую строку в конец Program.cs:
public partial class Program { }
Чтобы сделать тесты Playwright как можно более универсальными, я создал абстрактный класс SelfHostedPageTest, который наследует класс PageTest, поставляемый с Playwright, и использовал там CustomWebApplicationFactory. Также я выставил адрес сервера для тестового класса, который наследует SelfHostedPageTest:
public abstract class SelfHostedPageTest<TEntryPoint> : PageTest where TEntryPoint : class
{
private readonly CustomWebApplicationFactory<TEntryPoint> _webApplicationFactory;
public SelfHostedPageTest(Action<IServiceCollection> configureServices)
{
_webApplicationFactory = new CustomWebApplicationFactory<TEntryPoint>(configureServices);
}
protected string GetServerAddress() => _webApplicationFactory.ServerAddress;
}
Собственно тест Playwright просто наследует SelfHostedPageTest вместо PageTest следующим образом:
public class PlayWithPlaywrightHomeTests : SelfHostedPageTest<Program>
{
public PlayWithPlaywrightHomeTests() :
base(services =>
{
// configure needed services, like mocked db access, fake mail service, etc.
}) { }
// ...
}
Как видите, я передаю тип Program в качестве универсального аргумента в SelfHostedPageTest. CustomWebApplicationFactory, который используется внутри, — это почти та же реализация, что и у Даниэля. Я только добавил универсальный аргумент для класса Program, а также возможность передавать конфигурацию сервиса через конструктор:
internal class CustomWebApplicationFactory<TEntryPoint> :
WebApplicationFactory<TEntryPoint> where TEntryPoint : class
{
private readonly Action<IServiceCollection> _configureServices;
private readonly string _environment;
public CustomWebApplicationFactory(
Action<IServiceCollection> configureServices,
string environment = "Development")
{
_configureServices = configureServices;
_environment = environment;
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment(_environment);
base.ConfigureWebHost(builder);
// Add mock/test services to the builder here
if(_configureServices is not null)
{
builder.ConfigureServices(_configureServices);
}
}
// ...
}
Теперь мы можем использовать GetServerAddress() для получения адреса сервера и передачи его в метод Page.GotoAsync():
[Test]
public async Task TestWithWebApplicationFactory()
{
var serverAddress = GetServerAddress();
await Page.GotoAsync(serverAddress);
await Expect(Page).ToHaveTitleAsync(new Regex("Home Page - PlayWithPlaywright"));
Assert.Pass();
}
Вот и все.
Чтобы опробовать его, достаточно вызвать dotnet test в командной строке или PowerShell или запустить соответствующий тест в обозревателе тестов.
Заключение
Результат в моем тестовом проекте выглядит следующим образом (все тесты были запущены, когда я находился оффлайн):

Один проваленный тест — это записанная тестовая сессия моего блога на https://asp.net-hacker.rocks/ а другой — демо-тест, который я нашел на https://playwright.dev. Пройденный тест — это тот, который использует CustomWebApplicationFactory.
Именно такого результата я и ожидал.
Пример вы найдете в моем репозитории GitHub.

Пингбэк: Большой учебник по Playwright