Какие знаешь паттерны проектирования автотестов?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Паттерны проектирования в автоматизации тестирования
В автоматизации тестирования, особенно в UI и API тестировании, активно применяются паттерны проектирования для создания поддерживаемого, масштабируемого и переиспользуемого кода. Вот ключевые паттерны, которые я использую в своей практике.
1. Page Object Model (POM) и его вариации
POM — фундаментальный паттерн для UI-автотестов. Каждая страница приложения инкапсулируется в отдельный класс, содержащий локаторы элементов и методы для взаимодействия с ними.
# Пример Page Object в Python + Selenium
class LoginPage:
def __init__(self, driver):
self.driver = driver
self.username_field = (By.ID, "username")
self.password_field = (By.ID, "password")
self.submit_button = (By.CSS_SELECTOR, "button[type='submit']")
def login(self, username, password):
self.driver.find_element(*self.username_field).send_keys(username)
self.driver.find_element(*self.password_field).send_keys(password)
self.driver.find_element(*self.submit_button).click()
return HomePage(self.driver) # Возвращаем объект следующей страницы
Эволюцией POM стали:
- Page Factory (упрощает инициализацию элементов в Selenium, хотя в современных версиях используется реже).
- Page Element или Loadable Component (добавляет явные ожидания загрузки страницы).
- Page Object + Component Object — выделение переиспользуемых компонентов (например, хедера, модальных окон) в отдельные классы.
2. Screenplay Pattern (или Actor Pattern)
Более современная и гибкая альтернатива POM, основанная на принципах поведенчески-ориентированного проектирования. Акцент смещается с "страниц" на "действия" и "способности" актера (пользователя).
// Пример на Java с Serenity BDD
Actor user = Actor.named("Петр");
user.can(BrowseTheWeb.with(webDriver));
user.attemptsTo(
Open.browserOn().the(LoginPage.class),
Enter.theValue("standard_user").into(LoginPage.USERNAME_FIELD),
Enter.theValue("secret_sauce").into(LoginPage.PASSWORD_FIELD),
Click.on(LoginPage.LOGIN_BUTTON)
);
Преимущества: лучшее разделение ответственности, высокая переиспользуемость шагов, тесты читаются как пользовательские сценарии.
3. Data-Driven Testing (DDT)
Паттерн, при котором тестовые данные отделены от тестовой логики. Это позволяет запускать один и тот же тест с множеством наборов данных.
import pytest
import pandas as pd
# Тест параметризуется данными из CSV
@pytest.mark.parametrize("username, password, expected", [
("user1", "pass1", True),
("locked_user", "pass", False),
])
def test_login(username, password, expected):
# ... логика теста
assert login_result == expected
Источники данных: CSV, JSON, Excel, БД. Часто используется в связке с Parameterized Tests в фреймворках (JUnit, TestNG, Pytest).
4. Fluent Interface / Chain of Invocations
Позволяет строить читаемые "цепочки вызовов", делая код тестов похожим на естественный язык.
// Пример с WebdriverIO
browser
.url('/login')
.$('#username').setValue('test')
.$('#password').setValue('pass123')
.$('button').click()
.expect($('.welcome-message')).toBeDisplayed();
5. Singleton
Часто применяется для управления драйвером браузера (Driver Manager) или клиентом API, чтобы гарантировать существование только одного экземпляра в рамках тестового запуска (или потока).
public class WebDriverManager {
private static ThreadLocal<WebDriver> driver = new ThreadLocal<>();
private WebDriverManager() {} // Приватный конструктор
public static WebDriver getDriver() {
if (driver.get() == null) {
driver.set(new ChromeDriver());
}
return driver.get();
}
}
6. Factory Method
Используется для создания объектов со сложной логикой инициализации. Классический пример — фабрика драйверов (для Chrome, Firefox, Safari) или фабрика страниц.
class WebDriverFactory:
@staticmethod
def get_driver(browser_name: str):
if browser_name == "chrome":
return ChromeDriver()
elif browser_name == "firefox":
return FirefoxDriver()
else:
raise ValueError(f"Unsupported browser: {browser_name}")
7. Strategy Pattern
Полезен, когда необходимо менять алгоритм или поведение в рантайме. Например, разные стратегии аутентификации в API-тестах (OAuth, Basic Auth, API Key) или разные стратегии генерации тестовых данных.
interface AuthStrategy {
authenticate(request: Request): Request;
}
class ApiKeyStrategy implements AuthStrategy {
authenticate(request: Request): Request {
request.headers.set('X-API-Key', process.env.API_KEY);
return request;
}
}
8. Decorator Pattern
Часто используется для добавления кросс-функционального поведения к тестам или шагам: логирование, тайминг, повторные попытки (retry), скриншоты при падении.
# Pytest fixture как декоратор для логирования
@pytest.fixture
def logged_test(request):
print(f"\nStarting test: {request.node.name}")
yield
print(f"\nFinished test: {request.node.name}")
@logged_test
def test_something():
assert True
9. Facade Pattern
Предоставляет упрощенный интерфейс к сложной подсистеме. В автотестах это может быть "Test Facade" или "API Client", который скрывает сложность низкоуровневых HTTP-запросов за простыми методами createUser(), getOrder().
Выбор паттерна зависит от контекста:
- POM/Screenplay — для сложных UI-проектов.
- DDT и Strategy — для тестов, чувствительных к данным.
- Singleton/Factory — для управления ресурсами.
- Decorator/Facade — для улучшения инфраструктуры тестов.
Главное — не стремиться применить все сразу, а использовать комбинацию паттернов, которая решает конкретные проблемы поддержки, читаемости и стабильности вашего тестового фреймворка. Правильно выбранные паттерны снижают стоимость поддержки тестов и ускоряют разработку новых сценариев.