Как сделать базовый тест-класс для Selenium тестов и выполнить инициализацию через JUnit RuleChain

от автора

Этой статьей мы продолжаем серию публикаций о том, как мы автоматизировали в одном из крупных проектов ЛАНИТ процесс ручного тестирования (далее – автотесты) большой информационной системы (далее – Системы) и что у нас из этого вышло.

Как эффективно организовать иерархию классов? Как распределить пакеты по проектному дереву? Как сделать так, чтобы забыть о мердж-конфликтах при команде в 10 человек? Эти вопросы всегда стоят при старте новой разработки и на них никогда не хватает времени.

Источник

В этой статье мы описываем структуру классов и организацию кода, которая позволила нам небольшими силами разработать более полутора тысяч end-2-end UI тестов на базе Junit и Selenium для крупной системы федерального значения. Более того, мы ее успешно поддерживаем и постоянно дорабатываем существующие сценарии.

Здесь вы сможете найти практическое описание структуры иерархии базовых классов автотестов, разбиения проекта по функциональной модели java-packages и шаблоны-образцы реальных классов.

Статья будет полезна всем разработчикам, которые разрабатывают автотесты на базе Selenium.

Эта статья является частью общей публикации, в которой мы описывали, как небольшой командой выстраивали процесс автоматизации UI тестирования и разрабатывали для этого фреймворк на базе Junit и Selenium.

Предыдущие части: 

Реализация базового класса для всех тестов и JUnit RuleChain

Концепция разработки автотестов, как было показано в предыдущей статье (Часть 2. Техническая. Архитектура и технический стек. Детали реализации и технические сюрпризы), базируется на идее фреймворка, при которой для всех автотестов предоставляется набор системных функций – они бесшовно интегрируются и дают возможность разработчикам автотестов концентрироваться на конкретных вопросах бизнес-реализации тест-классов.

Фреймворк включает следующие функциональные блоки:

  • Rules – инициализация и финализация тестовых инфраструктурных компонентов как инициализация WebDriver и получение видеотеста. Более подробно описаны далее;
  • WebDriverHandlers – вспомогательные функции для работы с веб-драйвером как исполнение Java Script или доступ к логам браузера. Реализованы как набор статических state-less методов;
  • WebElements – библиотека типовых веб-элементов или их групп, которая содержит требуемую cross-function функциональность и типовое поведение. В нашем случае к такой функциональности относится возможная проверка завершения асинхронных операций на стороне веб-браузера. Реализованы как расширения веб-элементов из библиотек Selenium и Selenide.

Инициализация тестового окружения. Rules

Ключевым классом для всех тест-классов является BaseTest, от которого наследуются все тест-классы. BaseTest-класс определяет Junit «ранер» тестов и используемый RuleChain, как показано далее. Доступ из прикладных тестовых классов к функциям, предоставляемым rule-классами, осуществляется через статические методы rule-классов.   

Образец кода BaseTest представлен на следующей врезке.

@RunWith(FilterTestRunner.class) public class BaseTest {     private TemporaryFolder downloadDirRule                                                                   = new TemporaryFolder(getSomething().getWorkingDir());       @Rule     public RuleChain rules = RuleChain             .outerRule(new Timeout(TimeoutEnum.GLOBAL_TEST_TIMEOUT.value(),                                                                        TimeUnit.SECONDS))             .around(new TestLogger())             .around(new StandStateChecker())             .around(new WaitForAngularCreator())             .around(downloadDirRule)             .around(new DownloaderCreator(downloadDirRule))             .around(new EnvironmentSaver())             .around(new SessionVideoHandler())             .around(new DriverCreator(downloadDirRule))             .around(new BrowserLogCatcher(downloadDirRule))             .around(new ScreenShooter())             .around(new AttachmentFileSaver())             .around(new FailClassifier())             .around(new PendingRequestsCatcher());      // набор методов провайдеров общесистемных данных из проперти файлов    final protected SomeObject getSomething() {         return Something.getData();    }    … } 

FilterTestRunner.class – расширение BlockJUnit4ClassRunner, обеспечивает фильтрацию состава исполняемых тестов на базе регулярных выражений по значению специальной аннотации Filter(value = «some_string_and_tag»). Реализация приведена далее.

org.junit.rules.Timeout – используется для ограничения максимального продолжения тестов. Должна устанавливаться первой, так как запускает тест в новой ветке.

TestLogger – класс, который позволяет тесту логировать события в формате json для использования в ELK-аналитике. Обогащает события данными теста из org.junit.runner.Description. Также дополнительно автоматически генерирует события для ELK в формате json для начала-завершения теста с его длительностью и результатом

StandStateChecker – класс, который проверяет доступность веб-интерфейса целевого стенда ДО инициализации веб-драйвера. Обеспечивает быструю проверку, что стенд доступен в принципе.

WaitForAngularCreator – класс, который инициализирует веб-драйвер хэндлер для контроля завершения асинхронных операций ангуляра. Используется для индивидуальной настройки «особых» тестов c длительными синхронными обращениями.

org.junit.rules.TemporaryFolder – используется для задания уникальной временной папки для хранения файлов для операций загрузки и выгрузки файлов через веб-браузер.

DownloaderCreator – класс, который обеспечивает поддержку операций выгрузки во временную директорию файлов, загруженных браузером и записанных через Sеlenoid видеофункцию.

EnvironmentSaver – класс, который добавляет в Allure-отчет общую информацию о тестовом окружении.  

SessionVideoHandler – класс, который выгружает файл видеотеста, при его наличии, и прикладывает к отчету Allure.

DriverCreator – класс, который инициализирует WebDriver (самый главный класс для тестов) в зависимости от установленных параметров – локальный, solenoid или ggr-selenoid. Дополнительно класс исполняет набор обязательных для нашего тестирования Java Scripts. Все правила, которые обращаются к веб-драйверу, должны инициализироваться после этого класса.

BrowserLogCatcher – класс, который считывает Severe сообщения из лога браузера, журналирует их для ELK (TestLogger) и прикладывает к Allure-отчету.

ScreenShooter – класс, который для неуспешных тестов снимает скриншот экрана браузера и прикладывает его к отчету аллюр как WebDriverRunner.getWebDriver().getScreenshotAs(OutputType.BYTES)

AttachmentFileSaver – класс, который позволяет приложить к Allure-отчету набор произвольных файлов, требуемых по бизнес-логике тестов. Используется для прикладывания файлов выгруженных или загружаемых в систему.

FailClassifier – особый класс, который пытается в случае падения теста определить, было ли это падение вызвано инфраструктурными проблемами. Проверяет наличие на экране (после падения) особых модальных окон типа «Произошла системная ошибка №ХХХХХХХХХ», а также системных сообщений типа 404 и тому подобное. Позволяет разделить упавшие тесты на бизнес-падения (по сценарию) или системные проблемы. Работает через расширение org.junit.rules.TestWatcher.#failed метод.

PendingRequestsCatcher – еще один особый класс, который пытается дополнительно классифицировать, было ли падение вызвано незавершенными, зависшими или очень длительными рест-сервисами между ангуляром и веб-фронтендом. Дополнительно к функциональному тестированию дает возможность определять проблемные и зависающие рест-сервисы при больших нагрузках, а также общую стабильность релиза. Для этого класс логирует в ELK все события с зависшими рест-запросами, которые он получает, запуская специальный js в браузере через открытый веб-драйвер.

Шаблон реализации тест-класса 

package autotest.test.<sub-system>; @Feature("Развернутое название подсистемы как в TMS") @Story("Развернутое название теста согласно TMS") @Owner("фамилия автоматизатора вносящего последние правки в тест кейс") @TmsLink("Номер теста согласно тест линку. Соответствует настроенному шаблону") public class <Сквозной номер тест кейса>_Test extends BaseTest {     /**     * Объявляем логин от которой проходит целевой тест     **/     Login orgTest;       /** Объявляем все логины участвующие в тесте как вспомогательные **/     Login loginStep1;     ...     Login loginStepN;          /**     * Здесь перечисляются бизнес-объекты которые требуются для проведения теста - Тестовая сцена     * ... для всех требуемых бизнес объектов     **/       /**     * Инициализация тестовой сцены     * Для каждого бизнес объекта необходимо вывести в отчет инициализированное значение     *       Utils.allure.message("Бизнес имя объекта в контексте тест-сценария", business_object)     * Если класс инициализируется как null, то его выводят в отчет с указанием на каком шаге он будет заполнен.     * Далее этот тип объекта должен быть дополнительно выведен в отчет в методах preconditions или actions     *       Utils.allure.message("Номер созданного документа заполняемого на шаге Х", documentNumber)      **/       @Step("Инициализация тестовых объектов")     private void init(Login login) {         some_business_object = // создание требуемого объекта в или вне зависимости от login         Utils.allure.message("Бизнес имя объекта в контексте тест-сценария", some_business_object)                             // ... для всех требуемых бизнес объектов           /** Получаем значения вспомогательных логинов */         loginStep1 = LoginFactory.get(_Some_Login_);         ...         loginStepN = LoginFactory.get(_Some_Login_);       }          /**     * Реализация конкретного теста     **/     @Test     @Filter("Название теста для использования фильтрации на уровне JUnit")     @DisplayName("Развернутое название теста согласно TMS")     public void <Сквозной номер тест кейса>_<Полномочие>_<Уникальный_номер_проверки>_Test() {        // Получаем значение тестовой организации         orgTest = LoginFactory.get(_Some_Login_);                    // Инициализируем тестовые данные в зависимости от логина         init(orgTest);              // Выполняем шаги тестовых сценариев-предусловий. Шаги предусловия не должны зависеть от значения логина         preconditions();                  // Выполняем шаги целевого тестового сценария         actions(orgTest);     }          /**     * Выполнение требуемого набора активности для предусловий теста     **/     @Step("Предварительные условия")     protected void preconditions() {         loginStep1.login();         new SomeAction().apply(someTestObject1, ..., someTestObjectN);             Utils.allure.message("Получено значение для - Бизнес имя объекта в контексте тест-сценария", someTestObjectN)         ...     }           /**     * Метод содержит декларативный перечень операций тестового сценария     */     @Step("Шаги теста")     protected void actions(Login testLogin) {         testLogin.reLogin();                  // Выполнение требуемой активности или набора активностей основного теста         new SomeAction().apply(someTestObject1, ..., someTestObjectN);     } } 

 

Шаблон реализации класса тест-сценария

package autotest.business.actions.some_subsystem;   public class SomeAction {                      // ИНИЦИАЛИЗИРУЕМ СТРАНИЦЫ     PageClassA pageA = new PageClassA();     PageClassB pageB = new PageClassB();       @Step("Полное наименование тестового сценария согласно TMS")     @Description("Краткое описание тестового сценария согласно TMS")     public void apply(someTestObject1, ..., someTestObjectN) {         //Шаги сценария по TMS         step_1(...);         step_2(...);         ...         step_N(...);     }       @Step("Наименование шага 1 тестового сценария согласно TMS")     private void step_1(...) {        pageA.createSomething(someTestObject1);// just as an example create     }       @Step("Наименование шага 2 тестового сценария согласно TMS")     private void step_2(...) {     pageA.checkSomething(someTestObject1);// just as an example     }                                  ... } 

Реализация класса фильтрации тестов FilterTestRunner

Здесь показана реализация расширения BlockJUnit4ClassRunner для фильтрации тестов на основании произвольных наборов тегов.

/**  * Custom runner for JUnit4 tests.  * Provide capability to do additional filtering executed test methods  * accordingly information that could be provided by {@link FrameworkMethod}  */   public class FilterTestRunner extends BlockJUnit4ClassRunner {     private static final Logger LOGGER = Logger.getLogger(FilterTestRunner.class.getName());     public FilterTestRunner(Class<?> klass) throws InitializationError {         super(klass);     }     /**     * This method should be used if you want to hide filtered tests completely from output     **/     @Override     protected List<FrameworkMethod> getChildren() {         //Получаем экземпляр фильтра, который сравнивает заданный фильтр со значением аннотации @Filter по требуемой логике         TestFilter filter = TestFilterFactory.get();         List<FrameworkMethod> allMethods = super.getChildren();         List<FrameworkMethod> filteredMethod = new ArrayList<>();         for (FrameworkMethod method: allMethods) {             if (filter.test(method)) {                 LOGGER.config("method [" + method.getName() +"] passed by filter [" + filter.toString() + "]"  );                 filteredMethod.add(method);             } else {                 LOGGER.config("method [" + method.getName() +"] blocked by filter [" + filter.toString() + "]" );             }         }         return Collections.unmodifiableList(filteredMethod);     }         /**     * This method should be used if you want to skip filtered tests but no hide them     @Override     protected boolean isIgnored(FrameworkMethod method) {         …         if (filter.test(method)) {             return super.isIgnored(method);         } else {            return true;         }}     */ } 

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

Кстати, будем рады пополнить свою команду. Актуальные вакансии вот здесь.


ссылка на оригинал статьи https://habr.com/ru/company/lanit/blog/480910/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *