Playwright

Игра с Playwright

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

Слева открыто окно приложения, справа - 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.

1 комментарий к “Игра с Playwright”

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

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

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