Найти в Дзене

Playwright на Java: Пишем стабильные UI-тесты для интернет-магазина на реальном примере

Playwright на Java: Пишем стабильные UI-тесты Ранее мы уже разбирали Playwright в связке с Java. В этой статье мы создадим с нуля автоматизированные тесты для демо-сайта Sauce Demo, используя Playwright для Java и лучшие инженерные практики. Проектирование тестов: ️Page Object Model (POM) Первый шаг к созданию поддерживаемых тестов — правильная архитектура. Page Object Model (POM) — это ключевой паттерн, который позволяет отделить логику тестов от деталей взаимодействия с элементами страницы. Каждая страница веб-приложения представляется в виде отдельного класса, который: Содержит локаторы элементов на этой странице. Инкапсулирует методы для действий пользователя (клик, ввод текста). Это значительно снижает дублирование кода и упрощает поддержку: если изменится дизайн страницы, правки нужно будет внести только в одном классе Page Object. Структура проекта для нашего примера с Sauce Demo будет выглядеть так: src
├── main/java/com/saucedemo/pages/
│ ├── LoginPage.java // Страница ло
Оглавление
Playwright на Java: Пишем стабильные UI-тесты
Playwright на Java: Пишем стабильные UI-тесты

Ранее мы уже разбирали Playwright в связке с Java.

В этой статье мы создадим с нуля автоматизированные тесты для демо-сайта Sauce Demo, используя Playwright для Java и лучшие инженерные практики.

Проектирование тестов: ️Page Object Model (POM)

Первый шаг к созданию поддерживаемых тестов — правильная архитектура. Page Object Model (POM) — это ключевой паттерн, который позволяет отделить логику тестов от деталей взаимодействия с элементами страницы. Каждая страница веб-приложения представляется в виде отдельного класса, который:

  • Содержит локаторы элементов на этой странице.
  • Инкапсулирует методы для действий пользователя (клик, ввод текста).

Это значительно снижает дублирование кода и упрощает поддержку: если изменится дизайн страницы, правки нужно будет внести только в одном классе Page Object.

Структура проекта для нашего примера с Sauce Demo будет выглядеть так:

src
├── main/java/com/saucedemo/pages/
│ ├── LoginPage.java // Страница логина
│ └── ProductsPage.java // Главная страница после входа
└── test/java/com/saucedemo/tests/
└── LoginTest.java // Наши тестовые сценарии

Реализация Page Objects для Sauce Demo

Вот как выглядят классы Page Object для сайта Sauce Demo. Этот код является рабочим и может быть скопирован в ваш проект.

1. LoginPage.java (Страница авторизации)

java:

package com.saucedemo.pages;

import com.microsoft.playwright.Page;

public class LoginPage {
private final Page page;

// 1. Объявляем локаторы элементов страницы
private final String inputUsername = "#user-name";
private final String inputPassword = "#password";
private final String buttonLogin = "#login-button";
private final String errorMessageContainer = ".error-message-container";

// Конструктор получает объект Page от Playwright
public LoginPage(Page page) {
this.page = page;
}

// 2. Метод для навигации на страницу логина
public void navigate() {
page.navigate("https://www.saucedemo.com/");
}

// 3. Основной метод для выполнения логина
public ProductsPage login(String username, String password) {
page.fill(inputUsername, username);
page.fill(inputPassword, password);
page.click(buttonLogin);
return new ProductsPage(page); // Возвращаем объект следующей страницы
}

// 4. Метод для получения текста ошибки (для негативных тестов)
public String getErrorMessage() {
return page.textContent(errorMessageContainer);
}
}

2. ProductsPage.java (Страница с товарами после успешного входа)

java:

package com.saucedemo.pages;

import com.microsoft.playwright.Page;
import com.microsoft.playwright.Locator;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;

public class ProductsPage {
private final Page page;

private final String labelTitle = ".title";
private final String buttonMenu = "#react-burger-menu-btn";
private final String linkLogout = "#logout_sidebar_link";

public ProductsPage(Page page) {
this.page = page;
}

// Проверяем, что заголовок страницы отображается корректно
public void verifyTitleIs(String expectedTitle) {
Locator title = page.locator(labelTitle);
assertThat(title).hasText(expectedTitle); // Используем умные ассерты Playwright
}

// Метод для выхода из системы
public void logout() {
page.click(buttonMenu);
page.click(linkLogout);
}

// Пример добавления товара в корзину
public void addFirstProductToCart() {
page.click("button:has-text('Add to cart'):near(#item_4_title_link)");
}
}

Пишем тесты с использованием JUnit 5

Playwright отлично интегрируется с популярными тестовыми фреймворками. Мы используем JUnit 5. Следующий тестовый класс демонстрирует как позитивные, так и негативные сценарии.

java:

package com.saucedemo.tests;

import com.microsoft.playwright.*;
import com.saucedemo.pages.LoginPage;
import com.saucedemo.pages.ProductsPage;
import org.junit.jupiter.api.*;

import static org.junit.jupiter.api.Assertions.*;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class LoginTest {

// Общие ресурсы для всех тестов в классе
static Playwright playwright;
static Browser browser;

// Изолированные ресурсы для каждого теста
BrowserContext context;
Page page;
LoginPage loginPage;

@BeforeAll
static void launchBrowser() {
playwright = Playwright.create();
browser = playwright.chromium().launch(
new BrowserType.LaunchOptions().setHeadless(false) // Установите true для CI
);
}

@AfterAll
static void closeBrowser() {
playwright.close();
}

@BeforeEach
void createContextAndPage() {
context = browser.newContext(); // Новый контекст для изоляции теста
page = context.newPage();
loginPage = new LoginPage(page);
loginPage.navigate();
}

@AfterEach
void closeContext() {
context.close();
}

@Test
@DisplayName("Успешный вход с валидными учетными данными")
void successfulLoginWithStandardUser() {
// Шаги теста, организованные через Page Objects
ProductsPage productsPage = loginPage.login("standard_user", "secret_sauce");

// Проверка (Assertion)
productsPage.verifyTitleIs("Products");

// Дополнительное действие
productsPage.addFirstProductToCart();
}

@Test
@DisplayName("Вход заблокированным пользователем показывает ошибку")
void loginWithLockedOutUserShowsError() {
// Выполняем действие, которое должно привести к ошибке
loginPage.login("locked_out_user", "secret_sauce");

// Проверяем, что сообщение об ошибке содержит ожидаемый текст
String errorText = loginPage.getErrorMessage();
assertTrue(errorText.contains("Sorry, this user has been locked out"));
}
}

Продвинутые техники и интеграция

Современный стиль с аннотацией @UsePlaywright

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

import com.microsoft.playwright.junit.UsePlaywright;
import com.microsoft.playwright.Page;
import org.junit.jupiter.api.Test;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;

@UsePlaywright
public class ModernLoginTest {

@Test
void shouldLoginAndSeeProducts(Page page) { // Page инжектируется автоматически
LoginPage loginPage = new LoginPage(page);
loginPage.navigate();

ProductsPage productsPage = loginPage.login("standard_user", "secret_sauce");

// Использование встроенных ассертов Playwright
assertThat(page.locator(".title")).hasText("Products");
}
}

Параллельный запуск тестов

Чтобы ускорить прогон тестов, их можно запускать параллельно. Для этого в файл src/test/resources/junit-platform.properties нужно добавить конфигурацию:

junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.default=same_thread
junit.jupiter.execution.parallel.mode.classes.default=concurrent

Настройка проекта (pom.xml)

Для сборки проекта необходим pom.xml файл.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.saucedemo</groupId>
<artifactId>playwright-java-demo</artifactId>
<version>1.0</version>

<dependencies>
<!-- Playwright -->
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.56.0</version> <!-- Актуальную версию смотрите на сайте -->
</dependency>
<!-- JUnit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.11.4</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

Заключение и лучшие практики

  1. Всегда используйте POM: Это инвестиция в будущее, которая окупится при первом же изменении вёрстки сайта.
  2. Полагайтесь на auto-wait Playwright: Не используйте Thread.sleep(). Встроенные ожидания элемента — одна из сильнейших сторон фреймворка.
  3. Изолируйте тесты с помощью BrowserContext: Создание нового BrowserContext для каждого теста — залог их независимости и стабильности.
  4. Пишите атомарные тесты: Каждый тест должен проверять одну конкретную функциональность и быть независимым от других.
  5. Используйте продвинутые локаторы: Отдавайте предпочтение стабильным локаторам, таким как page.getByRole() или page.getByTestId(), вместо хрупких XPath.

Приведённые в статье примеры кода являются полноценными и готовыми к запуску. Они демонстрируют, как сочетание мощного API Playwright и правильных архитектурных решений позволяет создавать быстрые, надёжные и легко поддерживаемые автоматизированные тесты для современных веб-приложений.

Быстро изучить Playwright от "А" до "Я" на профессиональном уровне можно в этом курсе.