Игра с Playwright

Что такое Playwright?

Playwright – это фреймворк для тестирования веб-интерфейса от компании Microsoft, в котором можно работать с различными языками. Playwright поддерживает JavaScript/TypeScript, Python, Java и, конечно же, C#. В нем также есть поддержка консольного браузера в различных браузерах. Он используется с фреймворками для модульного тестирования, и поэтому его можно запустить в рамках конвейера CI/CD. Синтаксис интуитивно понятен, и мне он очень нравится. Кроме того, документация очень хороша и помогает легко начать с ним работу.

В этой статье я не буду знакомить вас с Playwright. На самом деле, веб-сайт и документация – гораздо лучший ресурс для знакомства. Я бы хотел поиграться с ним и использовать его по-другому. Вместо того чтобы тестировать уже загруженное на хост веб-приложение, я бы хотел тестировать веб-приложение, которое размещается в тестовом проекте с помощью фабрики WebApplicationFactory. Таким образом, вы получите действительно изолированные UI-тесты, которые не связаны с другой инфраструктурой и не упадут из-за проблем с сетью.

Работает ли это?

БЕСПЛАТНО СКАЧАТЬ КНИГИ в телеграм канале "Библиотека тестировщика"

Давайте попробуем:

Настройка решения

Следующие строки создают проект 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, в котором вы можете записать тестовый сценарий. Во время перехода по приложению справа будет сгенерирован тестовый код:

Вместо того чтобы тестировать внешний сайт, как это делал я, вы можете вызвать codegen на локально запущенном приложении.

Просто скопируйте сгенерированный код в тестовый проект NUnit и исправьте пространство имен и имя класса так, чтобы они соответствовали пространству имен вашего проекта.

Используя сгенерированный код в качестве примера, вы сможете написать больше тестов вручную.

Если это сделано, запустите dotnet test, чтобы выполнить сгенерированный тест и убедиться, что Playwright работает.

Начинаем играться

Обычно Playwright тестирует приложения, запущенные на сервере. При этом возникает одна простая проблема: если тест не может подключиться к запущенному приложению из-за проблем с сетью, о упадет. Как правило, тест должен иметь только одну единственную причину для “фейла”: он упадет, если не работает в соответствии с ожидаемым результатом.

Решением может стать тестирование веб-приложения, размещенного на той же инфраструктуре и в рамках того же процесса, что и реальное тестирование.

Microsoft уже предоставила возможность написать интеграционные тесты для веб-приложения с помощью WebApplicationFactory. Моя идея заключалась в том, чтобы использовать эту WebApplicationFactory для хостинга приложения, которое можно тестировать с помощью Playwright.

Поскольку WebApplicationFactory также предоставляет HttpClient, я ожидал бы получить URL для подключения. Этот HttpClient будет иметь BaseAddress, который я могу использовать для передачи в 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.

Если вы нашли какую-либо ошибку в этом посте, не стесняйтесь сообщить мне об этом:  сообщите о проблеме на GitHub или отредактируйте эту страницу на GitHub и отправьте мне PullRequest.

Перевод статьи «Play with Playwright».

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *