Разделы портала

Онлайн-тренинги

.
Нужен ли вам Cucumber с Selenium?
17.09.2020 00:00

Автор: Баз Дейкстра (Bas Djikstra)
Оригинал статьи
Перевод: Ольга Алифанова

Недавно я смотрел вебинар "Убивает ли Cucumber-автоматизация ваш проект" от SauceLabs, представленный Николаем Адволодкиным. В этом вебинаре Николай демонстрировал интересные цифры: 68% опрошенных сообщали, что они не сотрудничают с коллегами, создавая спецификации на сессии "трех товарищей". Однако 54% опрошенных сказали, что пользуются Cucumber.

Это означает, что значительное количество участников опроса, использующих Cucumber, не сотрудничают с другими при создании спецификаций путем таких практик, как сессии трех товарищей, спецификации на основании примеров и преобразования примеров. Однако это не сильная сторона Cucumber. Эти инструменты разворачиваются во всю мощь, когда используются для поддержки сотрудничества, как сказано в этой статье Аслака Хеллесой, создателя и ключевого разработчика проекта Cucumber.

Должен сказать, что эта статистика меня не удивляет. Многие мои клиенты используют Cucumber (или SpecFlow) таким же образом. В чем причина?

"Мы хотим, чтобы вся команда понимала, что мы тестируем нашими тестами".

Долгое время я тоже это поддерживал, думая, что использование Cucumber поверх кода тест-автоматизации – хорошая идея даже при отсутствии BDD. Я даже писал статью в блог Cucumber.io, которая говорит что-то в этом роде. Да, я указал, чего надо избегать и о чем подумать, но я не думаю, что этот пост хорошо отражает мою нынешнюю точку зрения.

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

Однако не в этом цель моей статьи. Я хочу показать ряд техник, которые можно применять, чтобы ваши тест-методы читались (почти) как литература, без нужды добавлять еще один абстрактный слой вроде Cucumber.

Мы снова будем тестировать ParaBank, наименее безопасный онлайн-банк в мире (то есть демо-приложение от Parasoft). В этом приложении можно выполнять ряд сценариев, связанных с онлайн-банкингом, вроде открытия нового депозитного или накопительного счета.

В Cucumber примерный сценарий, описывающий поведение ParaBank при открытии нового счета, будет выглядеть как-то так:

ЕСЛИ Джон – существующий пользователь ParaBank

И у него есть существующий депозитный счет с балансом в 5000 долларов

КОГДА он открывает новый сберегательный счет

ТОГДА показывается подтверждающее сообщение с номером нового счета.

Неплохо, да? Это читабельно, написано обычным языком, и (если вы знаете, что для депозита на новый счет нужен исходный баланс) описывает желаемое поведение ясно и непротиворечиво.

Но вот в чем штука: если это не было написано до того, как было создано ПО, через трех товарищей, спецификацию через пример и преобразование примеров, это вам не нужно. Можно отлично писать тестовый код, который будет почти так же читабелен, без дополнительного абстрактного слоя и зависимостей, то есть инструмента вроде Cucumber.

Если инженер-автоматизатор – единственный, кто читает спецификации, зачем вообще морочиться с их созданием? Это только создает проблему поддержки, без которой множество проектов может обойтись.

Приведу пример – вот так тот же самый тест будет выглядеть без слоя Cucumber, но с рядом дизайн-решений, включенных для читабельности (а это важный аспект тестового кода), которые я ниже опишу подробнее.

private WebDriver driver;
@Before
public void initializeDatabaseAndLogin() {
ApiHelpers.initializeDatabaseBeforeTest();
driver = DriverHelpers.createADriverOfType(DriverType.CHROME);
Credentials johnsCredentials = Credentials.builder().username("john").password("demo").build();
new LoginPage(driver).
load().
loginUsing(johnsCredentials);
}
@Test
public void openAccount_withSufficientFunds_shouldSucceed() {
Account aNewCheckingAccount =
Account.builder().type(AccountType.CHECKING).build();
Account depositingFromAccount =
Account.builder().id(12345).build();
new OpenAccountPage(driver).
load().
open(aNewCheckingAccount, depositingFromAccount);
boolean newAccountIdIsDisplayed = new OpenAccountResultPage(driver).newAccountIdIsDisplayed();
assertThat(newAccountIdIsDisplayed).isTrue();
}

Не знаю, как вам, но для меня это почти так же читабельно, как вышеуказанный сценарий Cucumber. Помните – если бы мы решили вместо этого использовать Cucumber, нам все равно пришлось бы писать тот же самый код. Поэтому если эти сценарии не обсуждаются заранее (в этом случае я просто назову их тестами), зачем морочиться с использованием Cucumber?

Давайте посмотрим, что я тут внедрил, чтобы сделать этот код как можно более читабельным:

Короткие тесты

Это, наверное, самое важное, и поэтому я в первую очередь это разберу. Ваши тесты должны быть краткими и внятными. В идеале они должны проверять только одну вещь. Нуждаетесь в определенных данных перед стартом самого теста? Попробуйте сделать это через API или напрямую в базе данных.

В этом примере я вызываю метод initializeDatabaseBeforeTest(), чтобы сбросить базу данных к известному состоянию через API. О том, почему тесты должны быть краткими, написана масса материала, поэтому не буду углубляться.

Моделируйте бизнес-концепции как типы в вашем коде

Если вы хотите создавать читабельные тесты, очень помогает моделировать бизнес-понятия, которые что-то значат для людей, как типы объектов в вашем коде. К примеру, в этом тесте мы создаем новый счет. Счет в контексте онлайн-банкинга – это сущность со специфическими свойствами. В этом случае у счета есть тип, уникальный идентификатор и баланс:

@Data
@Builder
@AllArgsConstructor
public class Account {
private AccountType type;
private int id;
private double balance;
public Account(){}
}

Я тут использую Lombok для генерации методов чтения и записи, а также в качестве средства сборки, позволяющего создание объектов в моем тест-методе.

Важно, чтобы все понимали и были согласны с определениями этих POJO (старых добрых Java-объектов) – таких, как объект Account. Это очень помогает людям, знакомым с кодом хуже автора, понять, что происходит. Отсутствие Cucumber не избавляет вас от общения с товарищами!

Еще подсказка: если свойство бизнес-объекта может принимать только особые значения, используйте enum, как мы сделали тут с AccountType:

public enum AccountType {
CHECKING,
SAVINGS
}

Это предотвращает случайное присвоение ложных значений объектам и свойствам, и в то же время повышает читабельность. Победа!

Хорошо подумайте над методами, которые раскрываются вашими PageObjects

Чтобы еще больше повычить читабельность, ваши Page Objects должны раскрывать (только) методы, у которых есть бизнес-смысл. В примере выше основной тест происходит на странице OpenAccount, где создается новый счет. Вместе с методом load(), используемым для переходу к странице напрямую (используйте его только для страниц, которые можно напрямую загрузить), тут есть метод open(), принимающий два аргумента, и оба они относятся к типу Account. Первый обозначает новый счет, второй – счет, с которого вносится депозит на новый счет.

Если вы посмотрите на страницу открытия счета в приложении ParaBank, то увидите, что там больше особо ничего и не сделать, поэтому имеет смысл раскрывать это действие в тест-методах, которые используют Page Object OpenAccount.

Используйте хорошие имена, а затем придумайте получше

Надеюсь, вы уже заметили, что для увеличения читабельности я старался тщательно выбирать имена в коде. Это трудно. Я менял имена моих переменных и методов много раз, прежде чем создать этот пример, и уверен, что там еще есть куда расти.

Длинные имена переменных и методов сами по себе неплохи, если не отклоняются от сути. Поэтому я назвал метод, открывающий новый счет на странице OpenAccount, как open() вместо openAccount().

Из контекста понятно, что мы открываем счет. Это метод на странице OpenAccount, и его аргументы принадлежат к типу Account. Нет нужды снова упоминать об этом в имени метода, как я сделал в предыдущей версии. Я узнал это из книги Clean Code, которую очень рекомендую автоматизаторам.

Используйте библиотеки, улучшающие читабельность

Помимо Lombok я также воспользовался библиотекой AssertJ, чтобы писать более внятные проверки. Поэтому вместо метода JUnit по умолчанию assertTrue() я теперь могу написать:

assertThat(newAccountIdIsDisplayed).isTrue();

и это, по-моему, проще читать. В AssertJ множество методов, помогающих писать более понятные проверки, и я думаю, что всем, кто пишет тесты на Java, стоит с ней познакомиться.

Итак, я надеюсь, что пример показал вам, что вполне возможно писать код (автоматизации), читабельный для человека, не добавляя дополнительный уровень абстракции в форме инструментов типа Cucumber или SpecFlow. Примеры из статьи выложены в GitHub – там также выложено еще несколько тестов, демонстрирующих читабельный (Selenium) тест-код.

Я уверен, что тут еще есть куда стремиться, и буду рад услышать ваши предположения, как еще можно улучшить читабельность этого примера кода. Однако моя основная мысль в том, что для читабельности кода вам не нужен Cucumber.

Обсудить в форуме