Здравствуйте.

В этой статье я бы хотел поделиться своим опытом автоматизации функционального тестирования. Речь пойдет о написании удобного и надежного тестового фреймворка.

Что будем использовать: 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