Какой у вас опыт автоматизации UI с Selenium WebDriver?

Ответ

Имею опыт создания стабильных и поддерживаемых фреймворков для автоматизации UI-тестов на основе Selenium WebDriver.

Стек и подходы:

  • Языки: Java (основной), Python.
  • Фреймворки: JUnit 5, TestNG, pytest.
  • Архитектура: Строгое следование Page Object Model (POM) и ее расширенной версии Page Factory для повышения переиспользуемости и читаемости кода.
  • Управление драйвером: Использование WebDriverManager для автоматической загрузки правильной версии бинарников (ChromeDriver, GeckoDriver).
  • Ожидания: Применение Explicit Waits (WebDriverWait) вместо Thread.sleep() для надежной работы с динамическим контентом.
  • Запуск: Параллельный запуск тестов через Selenium Grid или Docker-контейнеры для ускорения прогона.
  • Отчетность: Интеграция с Allure Report или ExtentReports для наглядных результатов.

Пример реализации Page Object на Java с явными ожиданиями:

// LoginPage.java - Класс, представляющий страницу логина
public class LoginPage {
    private WebDriver driver;
    private WebDriverWait wait;

    // Локаторы
    @FindBy(id = "username")
    private WebElement usernameField;
    @FindBy(id = "password")
    private WebElement passwordField;
    @FindBy(css = "button[type='submit']")
    private WebElement loginButton;
    @FindBy(css = ".alert-error")
    private WebElement errorMessage;

    public LoginPage(WebDriver driver) {
        this.driver = driver;
        this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
        PageFactory.initElements(driver, this);
    }

    // Метод для выполнения логина
    public HomePage login(String username, String password) {
        usernameField.sendKeys(username);
        passwordField.sendKeys(password);
        loginButton.click();
        return new HomePage(driver); // Возвращаем объект следующей страницы
    }

    // Метод для проверки сообщения об ошибке
    public String getErrorMessage() {
        wait.until(ExpectedConditions.visibilityOf(errorMessage));
        return errorMessage.getText();
    }
}

// LoginTest.java - Сам тест
@Test
public void successfulLoginTest() {
    WebDriver driver = new ChromeDriver();
    driver.get("https://app.example.com");

    LoginPage loginPage = new LoginPage(driver);
    HomePage homePage = loginPage.login("validUser", "validPass");

    Assert.assertTrue(homePage.isUserMenuDisplayed(), "После логина должен отображаться меню пользователя");
    driver.quit();
}

Решаемые проблемы:

  • Хрупкие тесты: Борьба с ними через явные ожидания и стабильные локаторы (предпочтительно data-testid атрибуты).
  • Поддержка: Четкое разделение логики теста и описания страниц облегчает обновление при изменении UI.

Ответ 18+ 🔞

А, опыт создания фреймворков для автотестов, говоришь? Ну, это ж святое дело, блядь! Когда все твои тесты разваливаются от одного чиха фронтендера, а ты потом три дня ищешь, какой же пидарас поменял id на class... Э, сабака сука!

Слушай, вот смотри, как я это обычно выстраиваю, чтобы не сойти с ума.

На чём строим этот бардак:

  • Основной язык: Java, конечно. Иногда Python, если совсем припёрло.
  • Фреймворки тестовые: JUnit 5, TestNG — классика, хуле. В питоне — pytest, там всё из коробки, красота.
  • Архитектура, мать её: Только Page Object Model (POM), и точка. А лучше её сестрёнку — Page Factory. Это чтобы не превратить код в свалку из driver.findElement(By.id("какая-то-хуйня")). Чистота, блядь, читаемость — залог того, что через месяц ты сам поймёшь, что написал.
  • Драйвер: WebDriverManager — просто песня, ёпта! Сам скачает нужный хромдрайвер, не надо вручную искать, какой из них подойдёт к твоей версии браузера. Спасение от ошибки «версия драйвера не соответствует», которая появляется в самый неподходящий момент.
  • Ожидания: Только Explicit Waits, WebDriverWait! Никаких Thread.sleep(5000)! Это, блядь, признак того, что человек сдался и пошёл по пути наименьшего сопротивления. А потом тест то пройдёт, то нет, потому что у кого-то интернет медленный. Пиздец, а не подход.
  • Запуск: Параллельно, через Selenium Grid или в Docker. Чтобы не ждать, пока 200 тестов по очереди отработают, как последние тормоза.
  • Отчёты: Allure или ExtentReports. Чтобы начальству красивые графики показать, а не сухой лог консоли тыкать. «Смотрите, мол, как мы работаем!».

Вот, смотри, как это выглядит в коде, чтобы не быть голословным:

// LoginPage.java - Это у нас страница входа, её мозги
public class LoginPage {
    private WebDriver driver;
    private WebDriverWait wait; // Наш спаситель, который ждёт элементы

    // Локаторы. Аннотации — магия PageFactory
    @FindBy(id = "username")
    private WebElement usernameField;
    @FindBy(id = "password")
    private WebElement passwordField;
    @FindBy(css = "button[type='submit']")
    private WebElement loginButton;
    @FindBy(css = ".alert-error")
    private WebElement errorMessage;

    public LoginPage(WebDriver driver) {
        this.driver = driver;
        this.wait = new WebDriverWait(driver, Duration.ofSeconds(10)); // Ждём 10 сек, не больше
        PageFactory.initElements(driver, this); // Инициализируем все элементы выше
    }

    // Метод логина. Ввёл данные, кликнул — получил следующую страницу
    public HomePage login(String username, String password) {
        usernameField.sendKeys(username);
        passwordField.sendKeys(password);
        loginButton.click();
        return new HomePage(driver); // Фокус! Возвращаем уже новую страницу
    }

    // Вытащить текст ошибки, но только когда она реально появится
    public String getErrorMessage() {
        wait.until(ExpectedConditions.visibilityOf(errorMessage)); // Вот он, Explicit Wait!
        return errorMessage.getText();
    }
}

// LoginTest.java - А это сам тест, который всё использует
@Test
public void successfulLoginTest() {
    WebDriver driver = new ChromeDriver();
    driver.get("https://app.example.com");

    LoginPage loginPage = new LoginPage(driver);
    HomePage homePage = loginPage.login("validUser", "validPass"); // Всё логика тут

    Assert.assertTrue(homePage.isUserMenuDisplayed(), "После логина должен отображаться меню пользователя");
    driver.quit(); // Не забываем закрыть, а то память жрать будет
}

Какие проблемы решаем, блядь:

  • Тесты-хрустальные вазы: Которые падают, если тень на экран упадёт. Боремся явными ожиданиями и вменяемыми локаторами. Лучше всего — договориться с фронтами и использовать data-testid. Мечта, а не атрибут!
  • Поддержка, ёпта: Когда дизайнер вчера опять «немного всё перерисовал». Если у тебя чётко разделено — тесты в одном месте, описание страниц в другом — ты меняешь локатор в одном классе LoginPage, и все 50 тестов, которые его используют, снова работают. А не ползаешь по сотне файлов, как мудак. Вот в чём сила подхода, понимаешь?