Domain Object with Lombok: Боевая классика +3



Domain Object (рус. «Доменный объект») — один из наиболее популярных подходов к использованию тестовых данных непосредственно в логике скриптов. На данный момент является одним из самых популярных и распространенных approach'ей, благодаря своей простоте, понятности и логичности.

Применим во всех видах автоматизации функционального тестирования (End-to-End, API, Integration), в независимости от проверяемой платформы, будь то Web, Mobile, или Desktop.

ВАЖНО: не стоит путать Domain Object с Data Transfer Object (DTO). Это абсолютно разные подходы, которые применяются в разных сферах.
В чем его суть?

Из другого названия подхода — «Business Object» — становится понятно, что это некая абстракция, представляющая собой модель и описание объекта, который важен именно для понимания и функционирования бизнес-логики приложения. Он не способен выполнять никаких функций, кроме «переноса» полей и их значений одного конкретного business-unit'а.
image



Как это выглядит


В качестве примера, возьмем любое приложение, где предусмотрено создание аккаунта пользователя. Именно пользователь и становится нашим доменным объектом. Практически во всех случаях, у юзера должны быть логин и пароль:

// Создаем доменный класс 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, а поддержка кода становится сплошным удовольствием.

Должны же быть минусы?

  1. Иммутабельность. А точнее, ее отсутствие. В этом данный подход с использованием Lombok проигрывает основном конкуренту — Builder (статья на Хабре). Зато, на мой взгляд, приведенный выше код является более «чистым», понятным, и эстетически приятным, по сравнению с нескончаемой цепочкой в билдере и нагромождением методов в Object-классе.
  2. Увеличение complexity там, где это не нужно. Не столько минус, сколько небольшое напоминание. Любой подход или паттерн имеет проблематику и должен решать некую проблему. Не стоит пытаться использовать Data Object в юнит-тесте, который всего лишь проверяет, что 2+2=4.

Большое спасибо за внимание. Буду рад отзывам и критике.

P.S. Расскажите в комментариях, какие подходы вы используете в работе. Будет очень интересно почитать.

Вы можете помочь и перевести немного средств на развитие сайта



Комментарии (3):

  1. ladutsko
    /#19836992

    сколько же энергии тратится на всякую фигню

  2. BUY33
    /#19837098 / +2

    Я даже не знаю, в 2019 году рассказывать про геттеры-сеттеры и как ими пользоваться.
    Допустим ваша целевая аудитория абсолютные новички, допустим вы случайно не заметили этой уймы статей про Lombok на хабре. Но, чёрт возьми, как же у меня подгорает, когда для новичков в статьях пишут о «магии», что всё работает волшебно, просто поставь аннотацию, «Просто, быстро, удобно». Хотите рассказать про Lombok? Так расскажите как он работает внутри, расскажите про Annotation Processing Tool, аннотацию поставить много ума не надо. Возможно вы сами в профессии недавно, но так это же какой отличный повод разобраться как оно устроено, как там работает внутри, что скрывают эти «волшебные» аннотации. Сходите к Lombok на гитхаб, посмотрите как оно устроено, разберитесь хотя бы с одной простейшей аннотацией. Вот это была бы статья!

    P.S. и да, Lombok умеет генерировать билдеры, «магическая» аннотация в наличии.

    • Haarolean
      /#19840736

      Хаб «гайды для самых маленьких».