Какие паттерны проектирования применяются в автотестах?

Ответ

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

1. Page Object Model (POM) — фундаментальный паттерн Каждая страница/компонент UI представляется классом, который инкапсулирует:

  • Локаторы элементов
  • Методы взаимодействия
  • Проверки состояния
public class LoginPage
{
    private readonly IWebDriver _driver;
    private readonly By _usernameField = By.Id("username");
    private readonly By _passwordField = By.Id("password");
    private readonly By _submitButton = By.CssSelector("button[type='submit']");

    public LoginPage(IWebDriver driver) => _driver = driver;

    public void EnterCredentials(string username, string password)
    {
        _driver.FindElement(_usernameField).SendKeys(username);
        _driver.FindElement(_passwordField).SendKeys(password);
    }

    public DashboardPage Submit()
    {
        _driver.FindElement(_submitButton).Click();
        return new DashboardPage(_driver); // Возвращает следующую страницу
    }
}

2. Page Factory (упрощённая реализация POM) Использование атрибутов [FindsBy] для автоматической инициализации элементов (хотя в Selenium 4 рекомендуется прямой подход).

3. Loadable Component Pattern Добавление явных ожиданий загрузки страницы/компонента.

public class DashboardPage : LoadableComponent<DashboardPage>
{
    protected override void ExecuteLoad() => Driver.Navigate().GoToUrl("/dashboard");
    protected override bool EvaluateLoadedStatus() => Driver.Title.Contains("Dashboard");
}

4. Singleton для WebDriver Гарантирует единственный экземпляр драйвера на весь тестовый прогон.

public sealed class WebDriverManager
{
    private static IWebDriver _instance;
    private static readonly object _lock = new object();

    public static IWebDriver Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (_lock)
                {
                    _instance ??= new ChromeDriver();
                }
            }
            return _instance;
        }
    }
}

5. Factory Method для создания драйверов

public interface IWebDriverFactory
{
    IWebDriver CreateDriver();
}

public class ChromeDriverFactory : IWebDriverFactory
{
    public IWebDriver CreateDriver() => new ChromeDriver();
}

6. Builder для тестовых данных

public class UserBuilder
{
    private string _name = "Default";
    private string _email = "default@test.com";

    public UserBuilder WithName(string name) { _name = name; return this; }
    public UserBuilder WithEmail(string email) { _email = email; return this; }
    public User Build() => new User(_name, _email);
}

// Использование
var user = new UserBuilder().WithName("John").WithEmail("john@example.com").Build();

7. Strategy для выбора стратегии тестирования Например, разные стратегии аутентификации (OAuth, JWT, Basic Auth).

8. Decorator для добавления логирования/ретров

public class LoggingWebDriverDecorator : IWebDriver
{
    private readonly IWebDriver _decoratedDriver;
    public LoggingWebDriverDecorator(IWebDriver driver) => _decoratedDriver = driver;

    public void FindElement(By by)
    {
        Console.WriteLine($"Finding element: {by}");
        return _decoratedDriver.FindElement(by);
    }
}

9. Composite для группировки тестовых шагов

10. Data-Driven Testing через Provider Pattern Отделение тестовых данных от тестовой логики.

Ключевой принцип: Паттерны должны упрощать код, а не усложнять его. Начинайте с POM и добавляйте другие паттерны только при реальной необходимости.

Ответ 18+ 🔞

А, слушай, вот эта вся хуйня про паттерны в автотестах — это ж на самом деле не просто умные слова, чтобы на собеседовании понтоваться. Это реально чтобы твой код не превратился в такую свалку, что потом сам через месяц разбираться будешь, как в тёмном лесу, и желания жить не останется. Ну, типа, чтобы поддерживать, читать и масштабировать это всё было не пиздец как больно.

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

public class LoginPage
{
    private readonly IWebDriver _driver;
    private readonly By _usernameField = By.Id("username");
    private readonly By _passwordField = By.Id("password");
    private readonly By _submitButton = By.CssSelector("button[type='submit']");

    public LoginPage(IWebDriver driver) => _driver = driver;

    public void EnterCredentials(string username, string password)
    {
        _driver.FindElement(_usernameField).SendKeys(username);
        _driver.FindElement(_passwordField).SendKeys(password);
    }

    public DashboardPage Submit()
    {
        _driver.FindElement(_submitButton).Click();
        return new DashboardPage(_driver); // Возвращает следующую страницу
    }
}

2. Page Factory — типа облегчённый POM на стероидах Раньше модно было через [FindsBy] всё делать, чтоб элементы сами инициализировались. Но сейчас, в Selenium 4, многие говорят, что проще без этих выкрутасов, напрямую. Но знать-то надо, мало ли легаси код попадётся, ебать его в сраку.

3. Loadable Component Pattern — чтобы не ждать как лох Чтоб страница или компонент явно говорили: "Всё, я загрузился, можно работать". А то без этого флакают тесты, потому что ты пытаешься кликнуть в то, чего ещё нет.

public class DashboardPage : LoadableComponent<DashboardPage>
{
    protected override void ExecuteLoad() => Driver.Navigate().GoToUrl("/dashboard");
    protected override bool EvaluateLoadedStatus() => Driver.Title.Contains("Dashboard");
}

4. Singleton для WebDriver — чтоб он был один на весь прогон Чтобы не получилось, что у тебя двадцать браузеров открыто, и комп тупит, как пьяный в говно. Гарантируем один экземпляр, и все довольны.

public sealed class WebDriverManager
{
    private static IWebDriver _instance;
    private static readonly object _lock = new object();

    public static IWebDriver Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (_lock)
                {
                    _instance ??= new ChromeDriver();
                }
            }
            return _instance;
        }
    }
}

5. Factory Method — для фанатов разнообразия Если тебе вдруг нужно то хром, то фаерфокс, то ещё какая-то дичь. Замутил фабрику — и плоди драйверы, какие душе угодно.

public interface IWebDriverFactory
{
    IWebDriver CreateDriver();
}

public class ChromeDriverFactory : IWebDriverFactory
{
    public IWebDriver CreateDriver() => new ChromeDriver();
}

6. Builder для тестовых данных — чтоб не ебаться с конструкторами Вместо того чтобы помнить порядок десяти параметров или создавать кучу перегрузок, ты просто нанизываешь методы, как бусы. Получается красиво и понятно.

public class UserBuilder
{
    private string _name = "Default";
    private string _email = "default@test.com";

    public UserBuilder WithName(string name) { _name = name; return this; }
    public UserBuilder WithEmail(string email) { _email = email; return this; }
    public User Build() => new User(_name, _email);
}

// Использование
var user = new UserBuilder().WithName("John").WithEmail("john@example.com").Build();

7. Strategy — когда логика может быть разной Ну, типа, если у тебя в тестах нужно аутентифицироваться по-разному: то OAuth, то JWT, то просто логин-пароль. Выбирай стратегию — и вперёд.

8. Decorator — чтобы обернуть драйвер и добавить фич Хочешь, чтобы каждый чих в лог писался? Или ретраи на падающие клики? Оберни драйвер в декоратор — и будет тебе счастье.

public class LoggingWebDriverDecorator : IWebDriver
{
    private readonly IWebDriver _decoratedDriver;
    public LoggingWebDriverDecorator(IWebDriver driver) => _decoratedDriver = driver;

    public void FindElement(By by)
    {
        Console.WriteLine($"Finding element: {by}");
        return _decoratedDriver.FindElement(by);
    }
}

9. Composite — чтобы собирать тестовые шаги в кучки Можно сложные сценарии из простых кубиков собирать. Удобно для тестов, которые повторяют одни и те же подготовительные действия.

10. Data-Driven Testing через Provider Pattern — святое дело! Отдели данные от логики, блядь! Не надо хардкодить сто одно значение прямо в тесте. Вынеси в провайдер, в файл, в базу — куда угодно. Тест должен быть один, а данных — овердохуища.

И главное, запомни, чувак: все эти паттерны — они как инструменты. Не надо брать кувалду, чтобы забить гвоздь в стену из гипсокартона. Начни с Page Object Model, а остальное подтягивай только тогда, когда реально почувствуешь, что без этого уже пиздец и больно. А то можно так наархитектурить, что сам потом плакать будешь.