Здравствуйте.
В этой статье я бы хотел поделиться своим опытом автоматизации функционального тестирования. Речь пойдет о написании удобного и надежного тестового фреймворка.
Что будем использовать: Java, Selenide, Alure, TestNG, Maven.

Проект на GitHub — SelenideBoilerplate.
Часто в статьях по автоматизации тестирования приводятся далекие от реальности примеры, например:
Есть много похожих примеров по ожиданиям, page object'ам и т.д. В итоге начинающему тестировщику может быть сложно правильно и удобно все организовать. Проект обрастает костылями из-за чего писать новые тесты и поддерживать старые становится все сложнее.
Также есть некоторые инструменты, которые на мой взгляд слишком многословны и переусложнены.
Я покажу простой, удобный и легко расширяемый тестовый фреймворк, работать с которым гораздо легче чем с обычным selenium и который я успешно использовал на нескольких проектах. Этот проект это основа, на реальных проектах все немного сложнее (паралеллизация, remoteDriver, много тестов и т.д.).

Начнем с модулей app и pages.
Обычно в примерах приводят довольно простые страницы, где все помещается в одном классе. Но на реальных проектах могут быть довольно большие страницы, описывать весь функционал которых в одном классе, не самая лучшая идея. Например это может быть страница пополнения счета с множеством форм разных платежных систем.
В этом случае лучше разбить страницу на несколько классов и даже элементов (например, товар в корзине) и собирать все это вместе в основном классе страницы.
Поэтому лучше создавать все страницы в одном месте, а именно в классе PageBuilder.
В классе AppConfig хранятся настройки тестируемого веб-приложения. Например — адрес сайта, тестовые пользователи и т.п. В этом проекте это просто адрес сайта.
 
Это главный класс в данном модуле. В конструкторе класса App создаются все страницы.
Благодаря такому подходу не нужно постоянно создавать page object'ы в тестах, создается только объект App из которого и достаются нужные страницы.
Также в классе App могут быть такие методы как — регистрация, оформление и создание заказа и т.п.
То есть большие операции в которых участвует несколько page object'ов и которые часто нужны в тестах.
Благодаря Selenide работать page object'ами очень просто. Все страницы наследуются от базового класса BasePage. В конструктор page object'а передается относительный url страницы.
Все элементы страницы имеют модификатор доступа public, благодаря чему можно писать тесты как в императивном так и в декларативном стилях. Также из элементов можно доставать необходимые данные, например текст или какой-то атрибут.
Локатор хранится только одном месте. Вся логика страницы должна быть описана в методах страницы.
При таком подходе, если что-то ломается или меняется, в большинстве случаев не нужно переписывать тесты, просто дорабатывается метод или локатор меняется на актуальный.
В модуле helpers хранятся 2 важных класса:
TestConfig — Из этого класса можно достать настройки с которыми запускаются тесты. Также здесь указаны настройки по умолчанию.
Тесты запускаются командой
Значения переменных достаются из командной строки и благодаря классу TestConfig становятся доступны в тестах и в приложении.
Можно например менять url приложения в зависимости от окружения (dev, stage, production).
Класс Driver это моя обертка над selenium и selenide драйверами с парой полезных методов.
Самое важные методы:
Driver.initDriver() — здесь инициализируется драйвер / браузер.
Все тестовые классы наследуются от класса A_BaseTest, в котором создается объект приложения App, логгер, softAssert, открывается и закрывается браузер, очищаются куки после каждого теста.
Также есть A_BaseTestListener в котором можно логировать ошибки.
Тесты выглядят примерно так. Легко читать, легко поддерживать.
Тестовые классы указываются в testng.xml.
В папке test-output хранятся логи и скриншоты — Driver.takeScreenshot().
Для отчетов используется Allure. После завершения тестов можно запустить команду
Проект на GitHub — SelenideBoilerplate
			  В этой статье я бы хотел поделиться своим опытом автоматизации функционального тестирования. Речь пойдет о написании удобного и надежного тестового фреймворка.
Что будем использовать: Java, Selenide, Alure, TestNG, Maven.

Введение
Проект на GitHub — SelenideBoilerplate.
Часто в статьях по автоматизации тестирования приводятся далекие от реальности примеры, например:
driver.get (“URL”)
driver.find_element_by_id(“ID”).send_keys(“username”)
driver.find_element_by_id (“ID”).send_keys(“password”)
driver.find_element_by_id(“submit”).click()
Есть много похожих примеров по ожиданиям, page object'ам и т.д. В итоге начинающему тестировщику может быть сложно правильно и удобно все организовать. Проект обрастает костылями из-за чего писать новые тесты и поддерживать старые становится все сложнее.
Также есть некоторые инструменты, которые на мой взгляд слишком многословны и переусложнены.
Я покажу простой, удобный и легко расширяемый тестовый фреймворк, работать с которым гораздо легче чем с обычным selenium и который я успешно использовал на нескольких проектах. Этот проект это основа, на реальных проектах все немного сложнее (паралеллизация, remoteDriver, много тестов и т.д.).
Инструменты
- Selenide — это библиотека для написания лаконичных и стабильных UI тестов с открытым исходным кодом. Selenide решает большую часть проблем с таймаутами, кликами на элементы которые не успели загрузиться и т.п. Также можно забыть про StaleElementReferenceException. Очень удобный и простой в освоении инструмент, поработав с которым уже не хочется возвращаться к selenium.
- WebDriverManager — Входит в Selenide. Библиотека которая берет на себя всю работу по скачиваю драйверов для браузера и установке путей к драйверам -System.setProperty("webdriver.browser.driver", "/path_to_driver/driver");
- Allure для отчетов.
- TestNG — тестовый фреймворк.
- Maven — инструмент для автоматизации сборки проектов.
Структура проекта

Начнем с модулей app и pages.
Класс PageBuilder
Обычно в примерах приводят довольно простые страницы, где все помещается в одном классе. Но на реальных проектах могут быть довольно большие страницы, описывать весь функционал которых в одном классе, не самая лучшая идея. Например это может быть страница пополнения счета с множеством форм разных платежных систем.
В этом случае лучше разбить страницу на несколько классов и даже элементов (например, товар в корзине) и собирать все это вместе в основном классе страницы.
Поэтому лучше создавать все страницы в одном месте, а именно в классе PageBuilder.
package app;
import app.pages.LoginPage;
public class PageBuilder {
    public static LoginPage buildLoginPage() {
        return new LoginPage("/login");
    }
    public static BalancePage buildBalancePage() {
        DepositForm depositForm = new DepositForm();
        WithdrawalForm withdrawalForm = new WithdrawalForm();
        return new BalancePage("/balance", depositForm, withdrawalForm);
    }
}
Класс AppConfig
В классе AppConfig хранятся настройки тестируемого веб-приложения. Например — адрес сайта, тестовые пользователи и т.п. В этом проекте это просто адрес сайта.
package app;
public class AppConfig {
    public static final String baseUrl = "https://google.com";
}
Класс App
Это главный класс в данном модуле. В конструкторе класса App создаются все страницы.
package app;
import app.pages.LoginPage;
public class App {
    public LoginPage loginPage;
    public App() {
        loginPage = PageBuilder.buildLoginPage();
    }
}
Благодаря такому подходу не нужно постоянно создавать page object'ы в тестах, создается только объект App из которого и достаются нужные страницы.
Также в классе App могут быть такие методы как — регистрация, оформление и создание заказа и т.п.
То есть большие операции в которых участвует несколько page object'ов и которые часто нужны в тестах.
Перейдем к page object'ам
Благодаря Selenide работать page object'ами очень просто. Все страницы наследуются от базового класса BasePage. В конструктор page object'а передается относительный url страницы.
Все элементы страницы имеют модификатор доступа public, благодаря чему можно писать тесты как в императивном так и в декларативном стилях. Также из элементов можно доставать необходимые данные, например текст или какой-то атрибут.
Локатор хранится только одном месте. Вся логика страницы должна быть описана в методах страницы.
При таком подходе, если что-то ломается или меняется, в большинстве случаев не нужно переписывать тесты, просто дорабатывается метод или локатор меняется на актуальный.
package app.pages;
import com.codeborne.selenide.SelenideElement;
import helpers.Driver;
import static com.codeborne.selenide.Selenide.*;
public class LoginPage extends BasePage {
    public SelenideElement loginField = $("#login__username");
    public SelenideElement passwordField = $("#login__password");
    public SelenideElement signInButton = $("#login_enter");
    public SelenideElement termsOfUseLabel = $("label[for=\"login_agree\"]");
    public LoginPage(String pageUrl) {
        super(pageUrl);
    }
    
    public void login(String email, String password) {
        loginField.setValue(email);
        passwordField.setValue(password);
        termsOfUseLabel.click();
        signInButton.click();
        Driver.waitForUrlContains("account/accounts");
    }
}
Остальное
В модуле helpers хранятся 2 важных класса:
TestConfig — Из этого класса можно достать настройки с которыми запускаются тесты. Также здесь указаны настройки по умолчанию.
Тесты запускаются командой
 mvn test -Dbrowser=chrome -Dheadless=1Значения переменных достаются из командной строки и благодаря классу TestConfig становятся доступны в тестах и в приложении.
Можно например менять url приложения в зависимости от окружения (dev, stage, production).
package helpers;
public class TestConfig {
    public static String browser = "chrome";
    public static String headless = "1";
    public static void initConfig() {
        browser = System.getProperty("browser") == null ? "chrome" : System.getProperty("browser");
        headless = System.getProperty("headless") == null ? "1" : System.getProperty("headless");
    }
    public static boolean isHeadless() {
        return headless.contains("1");
    }
}
Класс Driver это моя обертка над selenium и selenide драйверами с парой полезных методов.
Самое важные методы:
Driver.initDriver() — здесь инициализируется драйвер / браузер.
   public static void initDriver() {
        // Get settings from command line
        TestConfig.initConfig();
        // Set settings for selenide browser
        Configuration.pageLoadStrategy = "eager";
        Configuration.browserSize = "1920x1080";
        Configuration.holdBrowserOpen = false;
        Configuration.screenshots = false;
        if(TestConfig.isHeadless()) {
            Configuration.headless = true;
        } else {
            Configuration.headless = false;
        }
        switch (TestConfig.browser) {
            case "chrome":
                Configuration.browser = Browsers.CHROME;
                break;
            case "firefox":
                Configuration.browser = Browsers.FIREFOX;
                break;
            default:
                Configuration.browser = Browsers.CHROME;
                break;
        }
    }
Driver.clearCookies()
Driver.close()
Тесты
Все тестовые классы наследуются от класса A_BaseTest, в котором создается объект приложения App, логгер, softAssert, открывается и закрывается браузер, очищаются куки после каждого теста.
Также есть A_BaseTestListener в котором можно логировать ошибки.
Тесты выглядят примерно так. Легко читать, легко поддерживать.
import org.testng.annotations.Test;
public class ExampleTest extends A_BaseTest
{
    @Test
    public void loginViaEmail() {
        app.loginPage.open();
        app.loginPage.login("email@email.com", "passwords");
        
        logger.info("Sample info message");
               
        softAssert.assertEquals(2,2);
        softAssert.assertAll();
    }
}
Тестовые классы указываются в testng.xml.
В папке test-output хранятся логи и скриншоты — Driver.takeScreenshot().
Для отчетов используется Allure. После завершения тестов можно запустить команду
allure serve target/allure-results и посмотреть отчет.Проект на GitHub — SelenideBoilerplate
 
           
 

DenBalDen
Как мне везет, что я натыкаюсь на нужные статьи, спасибо!