Cucumber 3 + Java

от автора

Несколько месяцев назад состоялся релиз Cucumber JVM 3.0.0. Новая версия призвана сделать работу с данным BDD фреймвоком более очевидной и гибкой. В данной статье я расскажу об изменениях и новых фичах, а также приведу примеры их использования.

Реализация Cucumber Expressions

В третьей версии Cucumber JVM стал доступен Cucumber Expressions — простой язык выражений для поиска подстрок в тексте.
В отличие от регулярных выражений этот язык оптимизирован для удобочитаемости, что в контексте Cucumber’а имеет больший смысл. Если вам нужна гибкость, то по-прежнему можно воспользоваться регулярными выражениями.

Например, у нас есть следующая фича:

# language: ru Функция: Передача аргументов различных типов    Сценарий:     * передадим в метод шага целое число 15     * передадим в метод шага "текст"     * передадим в метод шага hello 

Для получения аргументов из неё можно воспользоваться следующим описанием шагов:

    @Допустим("передадим в метод шага целое число {int}")     public void giveInt(Integer int1) {         System.out.println(int1);     }      @Допустим("передадим в метод шага {string}")     public void giveString(String string) {         System.out.println(string);     }      @Допустим("передадим в метод шага {word}")     public void giveWord(String string) {         System.out.println(string);     } 

Как видно из примера Cucumber Expressions состоит из двух фигурных скобок с указанием типа передаваемого значения.

«Из коробки» доступна передача следующих типов:

  • {int}
  • {float}
  • {string}
  • {word}
  • {biginteger}
  • {bigdecimal}
  • {byte}
  • {short}
  • {long}
  • {double}

{string} соответствует строке в кавычках, а {word} — одному слову без кавычек (на момент написания статьи в выражение {word} можно передавать только слова, написанные латинскими буквами).

Можно создать свой тип данных, например мы хотим передавать из фичи объект класса LocalDate:

# language: ru Функция: Передача пользовательского типа    Сценарий:     * передадим в метод дату 01.06.2018 
    @Допустим("передадим в метод дату {localdate}")     public void передадим_в_метод_дату(LocalDate localdate) {         System.out.println(localdate.format(DateTimeFormatter.ofPattern("dd-MM-yyyy")));     } 

Для этого в пакете, указанном в glue, необходимо создать класс, реализующий интерфейс TypeRegistryConfigurer, и через него добавить свой тип данных в реестр:

public class TypeRegistryConfiguration implements TypeRegistryConfigurer {     @Override     public Locale locale() { // требуется только для определения формата разделителя в float и double         return new Locale("ru");      }      @Override     public void configureTypeRegistry(TypeRegistry typeRegistry) { // добавление в реестр определения необходимого типа         typeRegistry.defineParameterType(new ParameterType<>( // название параметра, используемое в определении шага:                 "localdate",  // регулярка, для поиска необходимого значения в фиче:                 "[0-9]{2}.[0-9]{2}.[0-9]{4}", // тип параметра:                  LocalDate.class, // функция, преобразующая входящую строку к нужному типу                 (Transformer<LocalDate>) s -> {                      DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");                     return LocalDate.parse(s, formatter);                 }         ));     } } 

Класс, реализующий интерфейс TypeRegistryConfigurer, в проекте должен быть один, иначе будет выброшено исключение.

Также Cucumber Expressions позволяет в скобках указать необязательный текст:

    @Допустим("Hello, world(s)!")     public void getHello() {         System.out.println("Hello world!");     }  

Внутри скобок нельзя использовать пробелы (на момент написания статьи поддерживается только латинский алфавит).

Альтернативный текст задается через косую черту:

    @Допустим("основной/альтернативный текст")     public void getAlternative() {         System.out.println("Hello world!");     } 
# language: ru Функция: Основной и альтернативный текст    Сценарий:     * альтернативный текст     * основной текст 

Экранировать {} и () можно обратной косой чертой.

В одном определении шага нельзя одновременно использовать Регулярные выражения и Cucumber-выражения.

Отказ от XStream

В первой и второй версии Cucumber для определения типа передаваемых данных использовались регулярные выражения и библиотека XStreamsConverters. В третьей версии Cucumber разработчики отказались от использования библиотеки XStreamsConverters.
Обоснованием для отказа от XStreamConverters была плохая документация, отсутствие возможности использования сторонних мапперов объектов, а также отсутствие поддержки Java 9.
Аннотации Delimiter, Format, Transformer и другие аннотации из XStream больше не работают. Вместо них теперь необходимо использовать ParameterType или DataTableType.

DataTable

Тип данных DataTable также подвергся изменениям.
Как и в предыдущих версиях Cucumber 3 без проблем справляется с преобразованием DataTable с одним столбцом в List, с двумя столбцами в Map и т.п. Но это работает, только если преобразовывать данные в один из следующих типов: String, Integer, Float, Double, Byte, Short, Long, BigInteger или BigDecimal.
Если же вы хотите создать из DataTable объект какого-то другого класса, то вам, как и в случае пользовательского типа данных, необходимо написать свой преобразователь:

# language: ru Функция: Передача пользовательского типа через DataTable    Сценарий:     Допустим у нас есть пользователи       | Василий | Чапаев | 09.02.1887 |       | Пётр      | Исаев   | 23.02.1890 | 
User.java

import java.time.LocalDate;  public class User {     private String firstName;     private String lastName;     private LocalDate birthDay;      public String getFirstName() {         return firstName;     }      public void setFirstName(String firstName) {         this.firstName = firstName;     }      public String getLastName() {         return lastName;     }      public void setLastName(String lastName) {         this.lastName = lastName;     }      public LocalDate getBirthDay() {         return birthDay;     }      public void setBirthDay(LocalDate birthDay) {         this.birthDay = birthDay;     }      @Override     public String toString() {         return "User{" +                 "firstName='" + firstName + '\'' +                 ", lastName='" + lastName + '\'' +                 ", birthDay=" + birthDay +                 '}';     } } 

    @Допустим("у нас есть пользователи")     public void у_нас_есть_пользователи(List<User> users) {         System.out.println(users);     } 

Преобразователь добавляется в реестр таким же образом, как и в примере с пользовательским типом данных:

public class TypeRegistryConfiguration implements TypeRegistryConfigurer {     @Override     public Locale locale() {         return new Locale("ru");     }      @Override     public void configureTypeRegistry(TypeRegistry typeRegistry) { // в этот раз в реестр добавляем DataTableType         typeRegistry.defineDataTableType(new DataTableType(                 User.class,                 (TableRowTransformer<User>) list -> {                     User user = new User();                     user.setFirstName(list.get(0));                     user.setLastName(list.get(1));                     DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");                     user.setBirthDay(LocalDate.parse(list.get(2), formatter));                     return user;                 }         ));     } } 

Before и After Step Hooks

Еще одно нововведение — пре и пост степы. Теперь можно определять хуки, которые будут вызываться до и/или после каждого шага сценария.
Step hooks работают по тем же правилам, что и хуки, относящиеся к уровню сценария:

  • на хуки можно навешивать тэги для их запуска только в определенных сценариях;
  • можно устанавливать очередность выполнения хуков;
  • AfterStep выполнится даже если шаг, после которого он должен был выполниться, сломается.
# language: ru Функция: Фикстуры    @hooks   Сценарий: вызов фикстур до и после сценария, а также перед и после каждого шага     Дано Первый шаг     Когда Второй шаг     Тогда Третий шаг    @only_scenario_hooks   Сценарий: вызов фикстур только до и после сценария     Дано Первый шаг     Когда Второй шаг     Тогда Третий шаг    @only_step_hooks   Сценарий: вызов фикстур только перед и после каждого шага     Дано Первый шаг     Когда Второй шаг     Тогда Третий шаг 
// секция not написана исключительно ради демонстрации // выполнится только перед сценариями с тэгами hooks и only_scenario_hooks     @Before(value = "(@hooks or @only_scenario_hooks) and not @only_step_hooks")     public void before() {         System.out.println("before scenario");     }  // выполнится перед каждым шагом в сценарии с тэгом only_step_hooks     @BeforeStep(value = "@only_step_hooks")     public void beforeStep() {         System.out.println("before step");     }  // выполнится после каждого шага в сценарии с тэгом only_step_hooks     @AfterStep(value = "not(@hooks or @only_scenario_hooks) and @only_step_hooks")     public void afterStep() {         System.out.println("after step");     }  // выполнится только после сценариев с тэгами hooks и only_scenario_hooks     @After(value = "@hooks or @only_scenario_hooks")     public void after() {         System.out.println("after scenario");     } 

В заключение хотелось бы сказать, что даже если вы использовали в своих проектах возможности XStream и с переходом на новую версию Cucumber необходимо будет внести небольшие доработки, рекомендую это сделать. Cucumber поддерживается и активно развивается, а новые фичи делают его использование все более гибким и понятным.

Ссылки:
Примеры из статьи
Моя статья по первой версии Cucumber JVM


ссылка на оригинал статьи https://habr.com/post/422651/


Комментарии

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

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