Playwright предоставляет мощные средства для автоматизации браузерных тестов, и один из важнейших аспектов разработки тестов — это организация кода таким образом, чтобы он был удобен для повторного использования. В тестировании на основе Playwright создание переиспользуемых компонентов значительно облегчает разработку, улучшает читаемость тестов и снижает дублирование кода.
При разработке тестов для Playwright важно разделять проект на логически обособленные компоненты. К основным элементам, которые часто становятся переиспользуемыми, можно отнести:
Создание таких компонентов помогает не только ускорить разработку тестов, но и упростить их поддержку и расширение.
Одним из наиболее популярных шаблонов проектирования для переиспользуемых компонентов является Page Object Model. В этом подходе для каждой страницы или крупного компонента приложения создается отдельный объект, который инкапсулирует взаимодействие с элементами UI. Вместо того, чтобы повторно прописывать селекторы и действия с ними в каждом тесте, используется Page Object, который делегирует выполнение действий на своей странице.
Пример использования Page Object для формы авторизации:
// authPage.js
class AuthPage {
constructor(page) {
this.page = page;
this.usernameField = page.locator('#username');
this.passwordField = page.locator('#password');
this.submitButton = page.locator('#submit');
}
async login(username, password) {
await this.usernameField.fill(username);
await this.passwordField.fill(password);
await this.submitButton.click();
}
}
module.exports = AuthPage;
В тесте можно использовать этот объект:
const { test, expect } = require('@playwright/test');
const AuthPage = require('./authPage');
test('Тестирование авторизации', async ({ page }) => {
const authPage = new AuthPage(page);
await authPage.login('user123', 'password123');
await expect(page).toHaveURL('/dashboard');
});
Компоненты UI включают в себя все элементы интерфейса, с которыми тесты взаимодействуют напрямую. Например, форма для ввода данных, кнопка или модальное окно. Часто эти компоненты можно сделать независимыми и легко переиспользуемыми через абстракции.
Пример компонента кнопки:
// buttonComponent.js
class Button {
constructor(page, selector) {
this.page = page;
this.selector = selector;
}
async click() {
await this.page.locator(this.selector).click();
}
async isVisible() {
return await this.page.locator(this.selector).isVisible();
}
}
module.exports = Button;
Пример использования компонента кнопки в тестах:
const { test, expect } = require('@playwright/test');
const Button = require('./buttonComponent');
test('Тестирование клика по кнопке', async ({ page }) => {
const button = new Button(page, '#submit');
await button.click();
await expect(page).toHaveURL('/success');
});
Создание таких компонентов повышает гибкость кода, так как при необходимости изменится только сам компонент, а не каждый тест отдельно.
Иногда в тестах требуется выполнение определенных действий, которые не зависят от конкретных страниц или компонентов, например, работа с базой данных или подготовка тестовых данных. Такие задачи можно выделить в отдельные функции-утилиты, которые затем можно повторно использовать в тестах.
Пример вспомогательной функции для ожидания загрузки элемента:
// utils.js
async function waitForElement(page, selector, timeout = 5000) {
await page.locator(selector).waitFor({ timeout });
}
module.exports = { waitForElement };
Пример использования функции-утилиты в тесте:
const { test, expect } = require('@playwright/test');
const { waitForElement } = require('./utils');
test('Тестирование ожидания элемента', async ({ page }) => {
await page.goto('/somepage');
await waitForElement(page, '#some-element');
await expect(page.locator('#some-element')).toBeVisible();
});
Для того чтобы сделать компоненты еще более универсальными, можно добавить параметризацию, позволяя передавать параметры в методы и конструкторы. Например, можно создать универсальный компонент формы, который будет работать с любыми формами на странице.
// formComponent.js
class Form {
constructor(page, formSelector) {
this.page = page;
this.form = page.locator(formSelector);
}
async fillField(fieldName, value) {
await this.form.locator(`[name=${fieldName}]`).fill(value);
}
async submit() {
await this.form.locator('button[type=submit]').click();
}
}
module.exports = Form;
Пример использования:
const { test, expect } = require('@playwright/test');
const Form = require('./formComponent');
test('Тестирование формы', async ({ page }) => {
const form = new Form(page, '#login-form');
await form.fillField('username', 'user123');
await form.fillField('password', 'password123');
await form.submit();
await expect(page).toHaveURL('/dashboard');
});
Playwright предоставляет гибкость для создания и использования классов, что позволяет реализовывать наследование для создания более сложных компонентов, которые могут повторно использовать базовый функционал.
Пример использования наследования для расширения функционала компонента кнопки:
// extendedButton.js
const Button = require('./buttonComponent');
class ExtendedButton extends Button {
constructor(page, selector) {
super(page, selector);
}
async doubleClick() {
await this.page.locator(this.selector).dblclick();
}
}
module.exports = ExtendedButton;
Пример использования расширенной кнопки в тесте:
const { test, expect } = require('@playwright/test');
const ExtendedButton = require('./extendedButton');
test('Тестирование двойного клика по кнопке', async ({ page }) => {
const button = new ExtendedButton(page, '#double-click-btn');
await button.doubleClick();
await expect(page).toHaveURL('/double-clicked');
});
В некоторых сценариях может быть необходимо протестировать приложение с несколькими пользователями или с разными набором данных. В таких случаях создание параметризированных компонентов и данных, которые можно подставлять в тесты, значительно упрощает задачу.
Пример создания компонента для тестирования разных пользователей:
// userComponent.js
class User {
constructor(page, username, password) {
this.page = page;
this.username = username;
this.password = password;
}
async login() {
await this.page.locator('#username').fill(this.username);
await this.page.locator('#password').fill(this.password);
await this.page.locator('#submit').click();
}
}
module.exports = User;
Пример использования:
const { test, expect } = require('@playwright/test');
const User = require('./userComponent');
test('Тестирование авторизации для разных пользователей', async ({ page }) => {
const user1 = new User(page, 'user1', 'password1');
await user1.login();
await expect(page).toHaveURL('/dashboard');
const user2 = new User(page, 'user2', 'password2');
await user2.login();
await expect(page).toHaveURL('/dashboard');
});
При создании переиспользуемых компонентов важно соблюдать следующие рекомендации: