Статьи

Введение в автоматизацию тестирования BDD с помощью Serenity и Cucumber-JVM

Serenity BDD  (ранее известный как  Thucydides ) — это библиотека отчетов с открытым исходным кодом, которая помогает вам писать более структурированные, более удобные в обслуживании критерии автоматического принятия, а также создает содержательные содержательные отчеты о тестах (или «живую документацию»), которые не только сообщают о результатах тестов, но и какие функции были проверены. В этой статье мы продолжим наше исследование Serenity и посмотрим, как он работает с популярным инструментом BDD  Cucumber-JVM .

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

1. Основы BDD

Поведенческая разработка, которая является основной концепцией, лежащей в основе многих функций Serenity. Команда, использующая Behavior Driven Development, использует беседы и совместную работу на конкретных примерах для выработки общего понимания функций, которые они должны создавать. Беседы о конкретных примерах и контрпримеры — отличный способ избавиться от любых скрытых предположений или недопонимания относительно того, что должна делать функция.

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

  In order to find items that I would like to purchase
  As a potential buyer
  I want to be able to search for items containing certain words

Чтобы сформировать общее понимание этого требования, вы можете обсудить несколько конкретных примеров. Разговор может идти примерно так:

  • «Итак, приведите пример того, как поиск может работать».
  • «Ну, если я ищу  шерсть , то я должен видеть только шерстяные изделия».
  • «Звук достаточно прост. Существуют ли другие варианты функции поиска, которые могли бы привести к другим результатам?»
  • «Ну, я мог бы также фильтровать результаты поиска; например, я мог искать только шерстяные изделия ручной работы».
  • «Таким образом, вы можете фильтровать по предметам ручной работы. Не могли бы вы привести примеры других типов товаров, по которым вы хотели бы фильтровать?»

И так далее. На практике многие из обсуждаемых примеров становятся «критериями приемлемости» для функций. И многие из этих критериев приемки становятся автоматическими приемочными испытаниями. Автоматизация тестов приемлемости обеспечивает ценную обратную связь для всей команды, так как эти тесты, в отличие от тестов модульных и интеграционных тестов, обычно выражаются в бизнес-терминах и могут быть легко понятны не разработчиками. И, как мы увидим далее в этой статье, отчеты, которые создаются при выполнении этих тестов, дают четкое представление о состоянии приложения.

2. Написание критериев приемки с помощью Cucumber-JVM

Cucumber  — популярный инструмент автоматизации тестирования BDD. Cucumber-JVM  — это Java-реализация Cucumber, на которой мы сосредоточимся в этой статье. В Cucumber вы выражаете критерии принятия в естественной, удобочитаемой форме. Например, мы могли бы написать пример «шерстяной шарф», упомянутый выше:

 Given I want to buy a wool scarf
 When I search for items containing 'wool'
 Then I should only see items related to 'wool'

Этот формат известен как  Gherkin и широко используется в Cucumber и других основанных на Cucumber инструментах BDD, таких как SpecFlow (для .NET) и Behave (для Python). Gherkin — это гибкий, легко читаемый формат, который может быть написан совместно с владельцами продуктов, чтобы гарантировать каждому. Слабо структурированный   формат « Дается когда» помогает людям сосредоточиться на том, чего они пытаются достичь, и на том, как они узнают, когда получат это.

Иногда таблицы могут использоваться для суммирования нескольких разных примеров одного и того же сценария. В Gherkin вы можете использовать примеры таблиц, чтобы сделать это. Например, следующий сценарий иллюстрирует, как вы можете искать различные типы изделий из разных материалов:

  Scenario Outline: Filter by different item types
    Given I have searched for items containing '<material>'
    When I filter results by type '<type>'
    Then I should only see items containing '<material>' of type '<type>'
  Examples:
    | material | type           |
    | silk     | Handmade       |
    | bronze   | Vintage        |
    | wool     | Craft Supplies |

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

3. Написание исполняемых спецификаций с Cucumber и Serenity

3.1. Написание сценария

Давайте начнем с первого примера, описанного выше. В Cucumber сценарии хранятся в  файлах объектов, которые содержат общее описание функции, а также ряд сценариев. Файл функций для приведенного выше примера вызывается search_by_keyword.featureи выглядит примерно так:

Feature: Searching by keyword

  In order to find items that I would like to purchase
  As a potential buyer
  I want to be able to search for items containing certain words

  Scenario: Should list items related to a specified keyword
    Given I want to buy a wool scarf
    When I search for items containing 'wool'
    Then I should only see items related to 'wool'

Эти файлы функций могут быть размещены в разных местах, но вы можете уменьшить объем конфигурации, который вам нужно сделать с Serenity, если вы поместите их в  src/test/resources/features каталог.

Вы обычно организуете файлы объектов в подкаталогах, которые отражают требования более высокого уровня. Например, в следующей структуре каталогов у нас есть определения функций для нескольких функций более высокого уровня:  search  и  shopping_cart :

|----src
| |----test
| | |----resources
| | | |----features
| | | | |----search
| | | | | |----search_by_keyword.feature
| | | | |----shopping_cart
| | | | | |----adding_items_to_the_shopping_cart.feature

3.2. Сценарий Бегун

Cucumber запускает файлы объектов через JUnit и нуждается в специальном классе для запуска тестов, чтобы фактически запускать файлы объектов. Когда вы запускаете тесты в Serenity, вы используете CucumberWithSerenity тестовый прогон  . Если файлы компонентов находятся не в том же пакете, что и класс тестового бегуна, вам также необходимо использовать этот @CucumberOptions класс для предоставления корневого каталога, в котором можно найти файлы компонентов  . Тестовый прогон для запуска всех файлов объектов выглядит следующим образом:

package net.serenity_bdd.samples.etsy.features;

import cucumber.api.CucumberOptions;
import net.serenitybdd.cucumber.CucumberWithSerenity;
import org.junit.runner.RunWith;

@RunWith(CucumberWithSerenity.class)
@CucumberOptions(features="src/test/resources/features")
public class AcceptanceTests {}

3.3. Определения шагов

В Cucumber каждая строка сценария Gherkin отображается на метод в классе Java, известный как определение  шага . Это использование аннотаций нравятся  @Given@When и  @Then матч линия в сценарии к методам Java. Вы определяете простые регулярные выражения для указания параметров, которые будут переданы в методы:

public class SearchByKeywordStepDefinitions {
    @Steps
    BuyerSteps buyer;

    @Given("I want to buy (.*)")
    public void buyerWantsToBuy(String article) {
        buyer.opens_etsy_home_page();
    }

    @When("I search for items containing '(.*)'")
    public void searchByKeyword(String keyword) {
        buyer.searches_for_items_containing(keyword);
    }

    @Then("I should only see items related to '(.*)'")
    public void resultsForACategoryAndKeywordInARegion(String keyword) {
        buyer.should_see_items_related_to(keyword);
    }
}

Эти определения шагов используют Serenity для организации кода определения шагов в более повторно используемые компоненты. @StepsАннотацию говорит Serenity , что эта переменная является библиотека Шаг. В Serenity мы используем Step Libraries, чтобы добавить слой абстракции между «что» и «как» в наших приемочных тестах. Определения шагов Cucumber описывают, «что» делает приемочный тест, в достаточно нейтральных для реализации условиях, благоприятных для бизнеса. Таким образом, мы говорим «ищет предметы, содержащие  « шерсть », а не« входит в  шерсть »«в поле поиска и нажимает на кнопку поиска». Этот многоуровневый подход облегчает понимание и сопровождение тестов и помогает создать большую библиотеку повторяющихся шагов бизнес-уровня, которые мы можем использовать в других тестах. Без этого В качестве многоуровневого подхода определения шагов имеют тенденцию очень быстро становиться очень техническими, что ограничивает повторное использование и затрудняет их понимание и поддержку.

Файлы определений шагов должны находиться внутри или под пакетом, содержащим бегуны сценария:

|----src
| |----test
| | |----java
| | | |----net
| | | | |----serenity_bdd
| | | | | |----samples
| | | | | | |----etsy
| | | | | | | |----features                                    
| | | | | | | | |----AcceptanceTests.java                      
| | | | | | | | |----steps                                     
| | | | | | | | | |----SearchByKeywordStepDefinitions.java
| | | | | | | | | |----serenity                                
| | | | | | | | | | |----BuyerSteps.java
Пакет бегуна сценария
Сценарист бегун
Определения шагов для участников сценария
Библиотеки Serenity Step находятся в другом подпакете.

3.4. Библиотеки «Шаг безмятежности»

Библиотека шагов Serenity — это обычный Java-класс с методами, помеченными  @Step аннотацией, как показано здесь:

public class BuyerSteps {

    HomePage homePage;                                          
    SearchResultsPage searchResultsPage;

    @Step                                                       
    public void opens_etsy_home_page() {
        homePage.open();
    }

    @Step
    public void searches_for_items_containing(String keywords) {
        homePage.searchFor(keywords);
    }

    @Step
    public void should_see_items_related_to(String keywords) {
        List<String> resultTitles = searchResultsPage.getResultTitles();
        resultTitles.stream().forEach(title -> assertThat(title.contains(keywords)));
    }
}
//end:tail
Библиотеки шагов часто используют объекты Page Objects, которые автоматически создаются
В  @Step аннотации указан метод, который будет отображаться как шаг в отчетах о тестировании.

В этих определениях шагов реализовано «что» за «как» шагов «задано когда». Однако, как и в любом хорошо написанном коде, определения шагов не должны быть слишком сложными и должны фокусироваться на работе на одном уровне. абстракция. Определения шагов обычно организуют вызовы к более техническим уровням, таким как веб-службы, базы данных или объекты страниц WebDriver. Например, в таких автоматизированных веб-тестах методы библиотеки шагов не вызывают непосредственно WebDriver, а скорее взаимодействуют с  Объекты страницы .

3.5. Объекты страницы

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

Serenity автоматически создает экземпляры Page Objects для вас и внедряет текущий экземпляр WebDriver. Все, что вам нужно беспокоиться, это код WebDriver, который взаимодействует со страницей. И Serenity предлагает несколько быстрых клавиш, чтобы сделать это проще. Например, вот объект страницы для домашней страницы:

@DefaultUrl("http://www.etsy.com")                      
public class HomePage extends PageObject {              

    @FindBy(css = "button[value='Search']")
    WebElement searchButton;

    public void searchFor(String keywords) {
        $("#search-query").sendKeys(keywords);          
        searchButton.click();                           
    }
}
Какой URL должен использоваться по умолчанию при вызове  open() метода
Объект страницы Serenity должен расширять  PageObject класс
Вы можете использовать  $ метод для прямого доступа к элементам с помощью выражений CSS или XPath.
Или вы можете использовать переменную-член с  @FindBy аннотацией

И вот второй объект страницы, который мы используем:

public class SearchResultsPage extends PageObject {

    @FindBy(css=".listing-card")
    List<WebElement> listingCards;

    public List<String> getResultTitles() {
        return listingCards.stream()
                .map(element -> element.getText())
                .collect(Collectors.toList());
    }
}
// end:tail[]

В обоих случаях мы скрываем реализацию WebDriver от способа доступа к элементам страницы внутри методов объекта страницы. Это делает код более легким для чтения и уменьшает количество мест, которые необходимо изменить, если страница изменена.

4. Отчетная и живая документация

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

отчет о безмятежности Кукеса

Рисунок 1. Результаты теста, представленные в Serenity

Обратите внимание, как этот отчет точно воспроизводит пример из беседы с бизнесом, а также дает возможность перейти к «что», чтобы увидеть, как был выполнен определенный шаг, и (в данном случае), как выглядят соответствующие снимки экрана. как.

Ранее мы видели, как примеры таблиц могут быть отличным способом обобщить бизнес-логику; важно, чтобы эти таблицы были отражены в результатах испытаний, как показано здесь:

отчет таблицы безмятежности Кукеса

Рисунок 2. Результаты теста с таблицей примеров в Serenity

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

Кьюкс спокойствие дома

Рисунок 3. Обзор результатов теста

В предыдущем разделе мы увидели, как файлы объектов были организованы в каталоги, которые представляют функции или возможности более высокого уровня. Serenity будет использовать эту структуру пакета для группировки и агрегирования результатов тестирования для каждой функции. Таким образом, Serenity может сообщить о том, насколько хорошо было протестировано каждое требование, а также рассказать о требованиях, которые  не  были протестированы:

требования кукес

Рисунок 4. Отчеты о безопасности и требованиях

5. Вывод

Надеюсь, этого будет достаточно, чтобы вы почувствовали вкус Serenity и Cucumber-JVM.

Тем не менее, мы едва нацарапали поверхность того, что Serenity может сделать для ваших автоматических приемочных тестов. Вы можете прочитать больше о Serenity и о принципах, лежащих в его основе, читая  Руководство пользователя или читая  BDD в действии , где эти главы посвящены нескольким главам. И не забудьте проверить онлайн-курсы в  Parleys .

Вы можете получить исходный код проекта, обсуждаемого в этой статье  на GitHub .