Перевод статьи «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