Паттерн Traits
Сложно переоценить важность паттернов. Они помогают писать код более структурировано, тратить меньше времени на отладку кода, позволяют новым людям в команде быстрее и проще влиться в процесс.
Как в разработке, так и в тестировании есть свои паттерны. И, безусловно, самым популярным и востребованным паттерном разработки автотестов является паттерн Page Object.
Page Object (далее PO) — паттерн проектирования автотестов, когда логика (методы, функции) отделены от самих тестов.
Данный паттерн позволяет более эффективно писать код, вызывая в тесте нужные нам методы:
productPage .clickDeliveryBtn() .clickCheckoutBtn();
Этот паттерн хорошо себя показывает ровно до момента, пока вы не попадаете на проект, где реализована технология SPA (Single Page Application).
Представим себе типичный интернет-магазин. На каждой странице есть подвал (далее footer) и шапка (далее header). В Header'е и в footer'е есть поле ввода текста (поиск).
Как нам в данном случае поступить? Использовать классический PO, описывая на каждой странице и footer, и header? Это будет нарушением принципа DRY (Don't Repeat Yourself), к тому же, при изменении одного из элементов (к примеру, в header был добавлен новый элемент) придется ходить по всем страницам и изменять в каждом классе.
Использовать некий, промежуточный класс? Тогда в некоторых классах, описывающих страницы, будут «лишние» методы. К примеру, на подробном описании страницы есть только footer, в корзине есть только header. Если у нас будет один общий класс, где описан header и footer, то на страницах подробного описания у нас будут методы для footer'a, что будет мешать в разработке (и может привести к некорректному поведению тестов).
Сделать несколько классов, где будут вариации наследования? Опять же, нарушение принципов DRY(а так же SOLID), а сколько будет вариаций при, скажем, 4 вариантов header'a и footer'a страшно представить.
На помощь нам приходит дальнейшая эволюция PO в виде WO (Widget Object), ScreenPlay, но, по моему мнению, они не удобны.
WO не удобен с точки зрения восприятия (если использовать «чистый» WO). Вот у нас не отработал виджет поиска. На какой странице это случилось? Как понять?
Использовать смешанное решение в виде PO + WO? Встает снова вопрос с наследованием. Как нам наследоваться от нескольких виджетов? Создать на каждой странице/тесте n виджетов? Опять же, нарушение DRY со всеми вытекающими.
Использовать паттерн ScreenPlay? Не все используют Cucumber/Serenity.
Паттерн Traits
Начиная с Java 8, в интерфейсах ради сохранения обратной совместимости был добавлен модификатор default. Данный модификатор позволяет в интерфейсе реализовать метод по умолчанию. Это и позволит нам реализовать паттерн Traits, ведь множественное наследование интерфейсов в Java не запрещено.
Паттерн Traits используется в объектно-ориентированном программировании, который представляет собой набор методов, которые могут быть использованы, чтобы расширить функциональные возможности класса.
Или, простыми словами, для каждого теста мы «собираем» свои методы, которые нам нужны для конкретного теста. Нам нужен header? Подключаем интерфейс с методами для header'a. Нам еще нужны методы для главной страницы? Нет ничего проще, подключаем интерфейс MainPage.
Простой пример использования данного паттерна. Создадим простейший интерфейс, который будет выводить нам текст "Here you trait!"
public interface Trait { default Trait giveMeTrait(){ System.out.println("Here you trait!"); return this; } }
И имплементируем его в наш класс:
public class MyTraitClass implements Trait{ @Test void testTrait(){ giveMeTrait(); } }
Готово. Таким образом мы можем подключить только нужные trait в тест, избавившись от ненужных в данном тесте методов.