Domain Object (рус. «Доменный объект») — один из наиболее популярных подходов к использованию тестовых данных непосредственно в логике скриптов. На данный момент является одним из самых популярных и распространенных approach'ей, благодаря своей простоте, понятности и логичности.
Применим во всех видах автоматизации функционального тестирования (End-to-End, API, Integration), в независимости от проверяемой платформы, будь то Web, Mobile, или Desktop.
Из другого названия подхода — «Business Object» — становится понятно, что это некая абстракция, представляющая собой модель и описание объекта, который важен именно для понимания и функционирования бизнес-логики приложения. Он не способен выполнять никаких функций, кроме «переноса» полей и их значений одного конкретного business-unit'а.

В качестве примера, возьмем любое приложение, где предусмотрено создание аккаунта пользователя. Именно пользователь и становится нашим доменным объектом. Практически во всех случаях, у юзера должны быть логин и пароль:
Особенно внимательные заметят, что все внутренние поля являются private. Задавать и считывать значения напрямую из полей объекта считается плохой практикой. Вместо этого принято использовать всем хорошо известные геттеры и сеттеры:
Теперь, мы получили доступ к полям, и можем задавать и считывать их значения. Но в данном примере у нас используется только два поля. А что произойдет, если этих полей будет пятнадцать? Верно, класс User разрастется до невиданных размеров, благодаря бесконечной копипасте getter'ов и setter'ов. Как же мы будем с этим справляться?
Здесь нам на помощь приходит Project Lombok — популярная библиотека, которая позволяет сократить код в несколько раз, избежать copy/paste-страданий, и значительно уменьшить количество времени на написание Data Object-классов. Несколько полезных ссылок:
Что же он делает? Автоматически создает геттеры и сеттеры для всех полей класса, путем присваивания ему соответствующей аннотации:
Просто, быстро, удобно. Таким образом, наш код становится гораздо читабельнее, классы — лаконичнее, а разработка — быстрее.
Хороший тест — это тест, в котором четко разделены тестовые данные, тестовая логика, и ее реализация.
Попробуем залогинить нашего пользователя. Создаем класс с тестовыми данными, и «наполняем» ими нового User'а:
Описываем модель Login-страницы:
Осталось только реализовать тестовый класс:
Готово! Вы великолепны!
В финале мы получаем аккуратную архитектуру проекта, с четким разделением логики, данных, и реализации. Lombok помогает избавиться от дублирования кода, Domain Object замечательно вписывается в философию Page Object, а поддержка кода становится сплошным удовольствием.
Должны же быть минусы?
Большое спасибо за внимание. Буду рад отзывам и критике.
P.S. Расскажите в комментариях, какие подходы вы используете в работе. Будет очень интересно почитать.
Применим во всех видах автоматизации функционального тестирования (End-to-End, API, Integration), в независимости от проверяемой платформы, будь то Web, Mobile, или Desktop.
ВАЖНО: не стоит путать Domain Object с Data Transfer Object (DTO). Это абсолютно разные подходы, которые применяются в разных сферах.В чем его суть?
Из другого названия подхода — «Business Object» — становится понятно, что это некая абстракция, представляющая собой модель и описание объекта, который важен именно для понимания и функционирования бизнес-логики приложения. Он не способен выполнять никаких функций, кроме «переноса» полей и их значений одного конкретного business-unit'а.

Как это выглядит
В качестве примера, возьмем любое приложение, где предусмотрено создание аккаунта пользователя. Именно пользователь и становится нашим доменным объектом. Практически во всех случаях, у юзера должны быть логин и пароль:
// Создаем доменный класс User public class User { // Объявляем, что User может содержать значение Login private String login; // Объявляем, что User может содержать значение Password private String password; }
Особенно внимательные заметят, что все внутренние поля являются private. Задавать и считывать значения напрямую из полей объекта считается плохой практикой. Вместо этого принято использовать всем хорошо известные геттеры и сеттеры:
public class User { private String login; private String password; // Интерфейс для присваивания значения полю login public void setLogin(String login) { this.login = login; } // Метод для получения значения login public String getLogin() { return this.login; } // Аналогично для пароля public void setPassword(String password) { this.password = password; } public String getPassword() { return this.password; } }
Теперь, мы получили доступ к полям, и можем задавать и считывать их значения. Но в данном примере у нас используется только два поля. А что произойдет, если этих полей будет пятнадцать? Верно, класс User разрастется до невиданных размеров, благодаря бесконечной копипасте getter'ов и setter'ов. Как же мы будем с этим справляться?
Магия Lombok
Здесь нам на помощь приходит Project Lombok — популярная библиотека, которая позволяет сократить код в несколько раз, избежать copy/paste-страданий, и значительно уменьшить количество времени на написание Data Object-классов. Несколько полезных ссылок:
Что же он делает? Автоматически создает геттеры и сеттеры для всех полей класса, путем присваивания ему соответствующей аннотации:
import lombok.Getter; import lombok.Setter; // Объявляем геттеры для всех полей класса User @Getter // Объявляем сеттеры для всех полей класса User @Setter public class User { private String login; private String password; }
Просто, быстро, удобно. Таким образом, наш код становится гораздо читабельнее, классы — лаконичнее, а разработка — быстрее.
Применяем в тесте
Хороший тест — это тест, в котором четко разделены тестовые данные, тестовая логика, и ее реализация.
Попробуем залогинить нашего пользователя. Создаем класс с тестовыми данными, и «наполняем» ими нового User'а:
// Создаем Test Data класс public class TestDataUser { // Объявляем private-константы с тестовыми данными private final static String DEFAULT_LOGIN = "vasiliy_pupkin"; private final static String DEFAULT_PASSWORD = "q1w2e3"; // Реализуем статический метод для получения дефолтного юзера public static User getDefaultUser() { // Создаем "чистого" пользователя User user = new User(); // Используем сеттер для присваивания значения Login user.setLogin(DEFAULT_LOGIN); // Используем сеттер для присваивания значения Password user.setPassword(DEFAULT_PASSWORD); // Возвращаем объект класса User, наполненного нужными нам данными return user; } }
Описываем модель Login-страницы:
public class LoginPage { // Создаем public-метод логина, который принимает объект User public void loginUser(User user) { // Через геттер получаем логин и передаем его в метод enterLogin() enterLogin(user.getLogin()); // Аналогично с паролем enterPassword(user.getPassword()); } private void enterLogin(String login) { this.loginInput.sendKeys(login); } private void enterPassword(String password) { this.passwordInput.sendKeys(password); } }
Осталось только реализовать тестовый класс:
public class LoginTest { @Test public void loginAsDefaultUser() { // Получаем объект с credentials тестового юзера User user = TestDataUser.getDefaultUser(); // Инициализируем Login-страницу LoginPage loginPage = new LoginPage(); // Логинимся, используя ранее полученный объект loginPage.loginUser(user); } }
Готово! Вы великолепны!
Подведем итоги
В финале мы получаем аккуратную архитектуру проекта, с четким разделением логики, данных, и реализации. Lombok помогает избавиться от дублирования кода, Domain Object замечательно вписывается в философию Page Object, а поддержка кода становится сплошным удовольствием.
Должны же быть минусы?
- Иммутабельность. А точнее, ее отсутствие. В этом данный подход с использованием Lombok проигрывает основном конкуренту — Builder (статья на Хабре). Зато, на мой взгляд, приведенный выше код является более «чистым», понятным, и эстетически приятным, по сравнению с нескончаемой цепочкой в билдере и нагромождением методов в Object-классе.
- Увеличение complexity там, где это не нужно. Не столько минус, сколько небольшое напоминание. Любой подход или паттерн имеет проблематику и должен решать некую проблему. Не стоит пытаться использовать Data Object в юнит-тесте, который всего лишь проверяет, что 2+2=4.
Большое спасибо за внимание. Буду рад отзывам и критике.
P.S. Расскажите в комментариях, какие подходы вы используете в работе. Будет очень интересно почитать.
