decorate image
decorate image
ОНЛАЙН-КУРС: РАЗРАБОТКА НА JMIX BPM
Все статьи
Содержание

    Опыт использования BDD в тестировании CUBA Platform

    Около семи лет назад Dan North в своей статье описал практическое применение BDD подхода, который позволяет сделать процесс разработки более понятным и управляемым путем налаживания внутренних коммуникаций. Индустрия с каждым днем проявляет всё больший интерес к этой методологии, нацеленной на продуктивное взаимодействие стандартных команд типа «аналитика-разработка-тестирование». Однако, сейчас лишь малая часть компаний решается на использование BDD. Почему?

    Итак, давайте разберемся. BDD (Behaviour Driven Development - «Разработка через поведение») гибкая методология, тесно связанная с TDD (Test Driven Development - "Разработка через тестирование"). По опыту, даже матерые тестировщики зачастую не видят разницы между этими методологиями. Действительно, на первый взгляд ее трудно вычленить: оба подхода предполагают написание документации и тестов до старта этапа разработки. А различие вот в чем: в BDD для описания тестов требуется использование естественного понятного языка, чтобы фактически объединить постановку задачи, тесты и документацию воедино. Другими словами, определяется DSL (специфичный предметно-ориентированный язык), потом составляется стандартный ограниченный набор фраз описывающих поведение нужных элементов. И с их помощью разрабатывается сценарий использования новой функциональности, который будет понятен всем.

    Давайте один раз увидим разницу, и она станет очевидной:

    Традиционный автотест (JUnit)

    @Before
    public void login() {loginAdmUser();}
    @After
    public void logOut() { closeWeb();}
    @Test //Test WorkFlow
    public void createInvoices() throws Exception {
    CreateClient createClient = _$( CreateClient.class);
    	createClient.createClient()););
    	Navigation navigation = _$( Navigation.class); );
            	navigation.openInvoicesMenu;
    CreateInvoices createInvoices = _$(CreateInvoices.class);
    	createInvoices.addInvoice(100);
    	createInvoices.verifiedTable(100);
    }
    

    Реализация действий в отдельном методе:

    public void createClient () {
    Client ClientWindow = _$(Client.class);
    	//Найти меню
    AppMenu appMenu = wire(AppMenu.class, "mainMenu");
    appMenu.openItem("projectName$Client.browse");
    	//создать клиента: открыть форму создания
        ClientWindow.getCreateClientButton().click();
    	//проверка наличия полей
        ClientWindow.getSurname()
            	.shouldBe(EDITABLE)
            	.shouldBe(ENABLED);
    	//заполнить поля
    final String uuid = UUID.randomUUID().toString().substring(0,6);
    String nameStringUUID= "User_"+ uuid; ClientWindow.getForename().setValue(nameStringUUID);
    ClientWindow.getSurname().setValue(uuid);
    	//Сохранить ClientWindow.getWindowCommitClientButton().click();  
    }
    

    BDD

    Invoices //название спецификации
    ==========================
    * Login as user
    
    Create invoice //название теста
    -------------------
    * Navigate to "Customers" -> ”Invoices"
    * Create Client
    * Click ”Create" button
    * Set ”Sum" to ”100"
    * Click "Save" button
    * Invoices should be
    |*|Invoice|Status|Sum |Date Created | Actions|
    |-|----|------|----|----------|-------|
    |*|CR-1|Active|100|*      	|*  	|
    ________
    * Logout
    

    Реализация шагов в отдельном файле:

    @Step("Navigate to <menu> -> <entry>")
      public void navigateToMenuEntry(String menu, String entry) {
    	Navigation.openMenu(menu);
    	Navigation.selectMenuEntry(entry);
      }
    

    Мы еще коснемся этого примера, а для начала давайте посмотрим на все разнообразие методологий, которые на данный момент имеют ненулевую актуальность.

    Сравним несколько методологий.

    Диаграмма ниже показывает сравнение трех подходов: TDD, TLD (Test Last Development) и BDD.

    text

    • BDD автотестирование и составление спецификации сопровождает каждый этап цикла разработки ПО, что обеспечивает постоянную актуальность автотестов и документации.
    • Методологии TDD и ATDD (Acceptance Testing) объединены на диаграмме в один блок, т.к. пишутся на этапе аналитики. Как уже было сказано выше TDD основан на написании тестов до разработки функционала.  Разработчик должен написать тесты для того, чтобы написать функционал под тест.
    • TLD (Test Last Development) включает тестирование после реализации функционала.
    • BDD универсален и может включаться на любом этапе разработки.

    На второй диаграмме изображено вовлечение участников процесса разработки в написание сценариев.

    text

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

    Несомненно, BDD - это хороший инструмент, позволяющий достичь качества продукта. Тесты и документация пишутся быстрее. Для бизнеса проект становится более прозрачным, благодаря конструкциям естественного языка, понятным любому человеку, далекому от программирования.

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

    BDD всем хорош, но почему его не используют?

    Ответ прост: это долго и дорого. С этим утверждением согласятся большинство IT компаний. И поначалу мы не были исключением. BDD неудобен хотя бы тем, что требует привлечения специалистов тестирования уже на этапе проработки требований.

    BDD переворачивает с ног на голову классическую схему ведения разработки (TLD). Она плохо реализуема, потому что это сложно. Удлиняется цикл разработки.

    BDD - это несомненно способ достичь качества. Но не все готовы платить временем и специалистами за это качество.

    Однако, что делать, если BDD все же хочется внедрить?

    Можно попробовать использовать готовые фреймворки. Например Cucumber, Squish, Yulup.

    Основная проблема сложности BDD не в процессе, а в реализации и существующих инструментах. Возьмем в качестве примера WEB разработку корпоративной информационной системы. Имея web реализацию мы сталкиваемся с WebDriver, являющимся в данный момент стандартом при автоматизации тестирования web-браузера. Он обладает довольно большими возможностями. Для учитывания различных кастомизаций элементов страницы необходимо придумывать варианты обращения к ним. И тут для облегчения разработки теста на помощь приходят различные библиотеки (Selenide, и др.), что создает свою экосистему, которую нужно знать.  Работая с WebDriver нужен программист, либо тестировщик-автоматизатор, т.к все реализуется с помощью кода и хитрых конструкций.

    Начало работы с BDD фреймворком - сложно и долго

    Наше внимание остановилось на инструменте под названием Gauge. Это гибкий и легковесный фреймворк, распространяющийся по свободной лицензии. Признаться честно, мы не особо изучали альтернативы, т.к. использование Guage было настойчиво продиктовано нашим заказчиком.

    В Gauge тесты пишутся в файлах спецификаций (файлы с расширением .spec). Спецификация содержит шаги теста, написанные на естественном языке. Эти шаги имплементируются на каком-либо языке программирования (у нас был использован язык программирования Java). При имплементации шагов важно соблюдение Naming Convention как в именах файлов сценария и реализации, так и в именах методов реализации и шагов сценария, они должны полностью совпадать. Дополнительную гибкость этому инструменту даёт то, что шаги могут иметь параметры.

    Gauge позволил нам использовать плюсы BDD. Однако, мы все равно столкнулись со сложностями, которые заключаются в сложности реализации: проблемы инструментария и внедрения процесса.

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

    Много времени у тестировщика уходило на код. А ведь еще тот же тестировщик должен был продумать поиск элементов на странице. Ситуация напоминала известную детскую игру: “Испорченный телефон”. Возникал коллапс. И мы решили: BDD будет работать, только в том случае, если тесты смогут писать аналитики. Нужно снизить трудоемкость написания тестов, упростить их. Но для этого нужно существенно упрощать интерфейсы тестирования. Инструменты должны быть проще, реализация процесса в совокупности со всеми подходами, библиотеками и так далее должны быть проще.

    Работа тестировщика вначале выглядела следующим образом:

    1. Изучение документации, если она есть; ❌
    2. Составление чеклиста;
    3. Ad-hoc тестирование;
    4. Составление тест плана;
    5. Уточнение картины мира у аналитика;❌
    6. Уточнение картины мира у разработчика;❌
    7. Если все срослось, написание тестовой документации, параллельно с тестированием;❌
    8. Ожидание фикса багов, тестирование багов;
    9. Описание страниц, контролов, поиск элементов на странице используя Web-Driver. Поиск того что уже реализовано в системе тестов;❌
    10. Написание логики теста;
    11. Релиз;
    12. Support bug/Regress bug;
    13. Обновление спецификации;❌
    14. Фикс бага;
    15. Обновление авто-теста, обновление большого количества изменившихся контролов;❌
    16. Релиз;
    17. ...

    Пункты отмеченные ❌ (1, 5, 6, 7, 9, 13, 15) приводят к временным затратам. Их можно и нужно оптимизировать.

    Этот список кратко проиллюстрирован на диаграмме процесса разработки:

    text

    Наша компания специализируется на проектах веб реализацией интерфейсов. Исходя из этого мы используем инструмент Web Driver для взаимодействия с веб браузером.

    Де-факто Selenium Web Driver является стандартом, и он используется для описания веб объектов на любых фреймворках, в том числе Gauge, jUnit, Masquerade и других. Гибкости у него много для разных задач, что создает излишнюю трудоемкость в локально-типовых задачах. Нам нужно найти решение для уменьшение трудоемкости.

    Для примера покажем на схеме — как связаны Selenium Web Driver, фреймворк Gauge, библиотека Masquerade, язык программирования Java:

    text

    В этой схеме можно вместо BDD фреймворка поставить jUnit, TestNG или любой другой, любая связка будет работать, в зависимости от потребностей. Selenium и Masquerade останется, язык программирования можно изменить.

    Ускорение процесса написания кода — подключение Masquerade

    В нашей компании разработка ведется на платформе CUBA. И специально для этой платформы был разработан инструмент для автотестов: Masquerade - библиотека, которая предоставляет лаконичный и удобный API для работы с кодом при имплементации тестов с использованием WebDriver. Эта библиотека работает над Selenium Web Driver, дружит с selenide и любыми фреймворками.

    В CUBA проектах каждый элемент веб страницы содержит cuba-id, который не меняется. В CUBA используется компонентный подход, а библиотека Masquerade упрощает взаимодействие с элементами веб страницы. Библиотека умеет совершать действия с элементами веб страницы реализованными с помощью CUBA — более простым образом. Поэтому при поиске элементов на странице не нужно использовать громоздкие конструкции с XPath, как было раньше:

    $(new By.ByXPath("//*/div/div[2]/div/div[2]/div/div/div[3]/div/div/div[3).click();
    

    Или более лаконичных конструкций, которые по прежнему громоздки:

    private static void click(String cssClass, String caption) {
              	 $(By.cssSelector(cssClass)
              	 .$(byText(caption))
    	      	.closest(".v-button")
    	      	.click();
    }
    

    Теперь, описание вложенного контрола выглядит просто и к нему легко обратиться. Можно даже не искать контрол на странице, т.к в проекте он уже есть. Приведем пример описания кнопки для формы авторизации в приложении:

    text

    В коде страницы нам виден четко узнаваемый элемент cuba-id=”loginButton”.

    text

    Опишем кнопку используя библиотеку Masquerade:

    @Wire(path = {"WebHBoxLayout", "loginButton"})	
    private Button loginButton;
    

    Простой вариант реализации теста на фреймворке jUnit — блок авторизации, который выполняется перед каждым тестом:

    @Before
    public void loginAdm() {
       Tests loginTest = _$(Tests.class);
       loginTest.login();
    }
    

    А в теле метода login следующий код:

    LoginWindow loginWindow = _$(LoginWindow.class);
    assertNotNull(loginWindow.getLoginField());
    loginWindow.getLoginField()
           .shouldBe(EDITABLE)
           .shouldBe(ENABLED);
    loginWindow.loginField.setValue("admin");
    loginWindow.passwordField.setValue("admin");
    loginWindow.rememberMeCheckBox.setChecked(true);
    loginWindow.loginButton().click();
    

    При этом самое важное — то как мы описываем страницу, как мы обращаемся к элементам. Описание страницы LoginWindow:

    public class LoginWindow extends Composite<LoginWindow> {
       @Wire(path = {"loginField"} )
       private TextField loginField;
       @Wire(path = {"passwordField"} )
       private PasswordField passwordField;
       @Wire(path = {"rememberMeCheckBox"} )
       private CheckBox rememberMeCheckBox;
       @Wire(path = {"loginFormLayout", "loginButton"}   )
       private Button loginButton;
       }
    

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

    getMaxResultsLayout().openOptionsPopup().select("5000")
    

    Или отсортировать таблицу:

    Table tb1 = client.getPaymentsTable();
    tb1.sort("column_year", Table.SortDirection.ASCENDING);
    

    Список некоторых действий с таблицей смотрите на скриншотах ниже:

    text

    text

    text

    Использование Masquerade значительно упростило написание тестов, теперь, чтобы написать тест для новой функциональности, нужно:

    1. С помощью Masquerade описать страницу - это делается легко и не требует особых навыков программирования.
    2. Собрать в одном классе все страницы, которые используются при проверке функционала.
    3. Из готовых конструкций естественного языка собрать тестовый сценарий (подставив туда названия нужных элементов), то есть написать Gauge-спецификацию.

    Интегрируем Masquerade и Gauge

    До использования BDD, применялся подход TLD и для работы с ним мы также оптимизировали процесс написания кода тестов. Использовали связки jUnit/TestNG + WebDriver+Selenide+Masquerade.

    Теперь, для того, что бы работать с Gauge, добавляем соответствующий плагин в intellij IDEA. После этого появится возможность создавать новый тип тестов - Specification.

    Теперь создаем спецификацию (сценарий) и имплементируем шаги используя возможности WebDriver, Masquerade и Java.

    text

    Кликаем на шаг сценария и переходим в имплементацию:

    ![text]/uploads/aa4f1b471afe4e57a80f641c377488ea.png)

    В имплементации можно использовать уже существующий метод login().

    Как же выглядит это совершенство?

    Для сравнения приведем пример автотеста написанный с использованием фреймворка jUnit (слева) и фреймворка Gauge:

    jUnit

    @Before
    public void login() {loginAdmUser();}
    @After
    public void logOut() { closeWeb();}
    @Test //Test WorkFlow
    public void createInvoices() throws Exception {
    CreateClient createClient = _$( CreateClient.class);
    	createClient.createClient()););
    	Navigation navigation = _$( Navigation.class); );
            	navigation.openInvoicesMenu;
    CreateInvoices createInvoices = _$(CreateInvoices.class);
    	createInvoices.addInvoice(100);
    	createInvoices.verifiedTable(100);
    }
    

    Реализация действий в отдельном методе:

     public void createClient () {
    Client ClientWindow = _$(Client.class);
    	//Найти меню
    AppMenu appMenu = wire(AppMenu.class, "mainMenu");
    appMenu.openItem("projectName$Client.browse");
    	//создать клиента: открыть форму создания
        ClientWindow.getCreateClientButton().click();
    	//проверка наличия полей
        ClientWindow.getSurname()
            	.shouldBe(EDITABLE)
            	.shouldBe(ENABLED);
    	//заполнить поля
    final String uuid = UUID.randomUUID().toString().substring(0,6);
    String nameStringUUID= "User_"+ uuid; ClientWindow.getForename().setValue(nameStringUUID);
    ClientWindow.getSurname().setValue(uuid);
    	//Сохранить ClientWindow.getWindowCommitClientButton().click();  
    }
    

    Gauge

    Invoices //название спецификации
    ==========================
    * Login as user
    
    Create invoice //название теста
    -------------------
    * Navigate to "Customers" -> ”Invoices"
    * Create Client
    * Click ”Create" button
    * Set ”Sum" to ”100"
    * Click "Save" button
    * Invoices should be
    |*|Invoice|Status|Sum |Date Created | Actions|
    |-|----|------|----|----------|-------|
    |*|CR-1|Active|100|*      	|*  	|
    ________
    * Logout
    

    Реализация шагов в отдельном файле:

     @Step("Navigate to <menu> -> <entry>")
      public void navigateToMenuEntry(String menu, String entry) {
    	Navigation.openMenu(menu);
    	Navigation.selectMenuEntry(entry);
      }
    

    “Navigation.openMenu(menu)” содержит реализацию открытия меню с помощью библиотеки Masquerade.

    Библиотека впоследствии была расширена и появились универсальные шаги, которые могут быть использованы для любого CUBA-приложения. Это шаги, позволяющие работать с элементами программы: кнопками, полями, таблицами. Эти универсальные шаги и стали тем набором стандартных фраз, которые мы используем в BDD для написания сценариев.

    Благодаря связке Masquerade+Gauge мы существенно снизили трудоемкость создания тестов. Теперь тесты могут писать люди, не имеющие особых навыков программирования. Тест может писать один человек (раньше сценарий придумывал один, а реализовывал - другой, что приводило к путанице). Итак, мы добились своей цели - интерфейсы упрощены, а аналитикам не составит труда писать тестовые сценарии.

    Изменения процесса изображены ниже:

    Было

    text

    Стало

    text

    Итоги

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

    Временные затраты на поддержку документации сокращаются пропорционально количеству измененных спецификаций, т.к одно изменение спецификации (логики системы) приводит автоматически к изменению автотеста за одну итерацию. Т.е. тестировщику не нужно лезть в систему документации (типа Confluence и т.д) для апдейта, и для других участников команды это тоже справедливо.

    Время на реализацию и поддержку тестов при наличии библиотеки, упрощающей работу с объектами страницы, уменьшилось в два раза, по сравнению с работой с обычным чистым web -driver и и затратами на переделку ссылок XP.

    Согласно фундаментальным знаниями по управлению качеством — стоимость устранения ошибок сбора требований и анализа растет экспоненциально. Соответственно вероятность получения проблем, связанных с переделкой продукта, согласно существующим статьям и графикам при итеративной разработке, при раннем обнаружении проблемы, которая заключается в хорошей проработке требований, снижает стоимость разработки  существенный процент, в зависимости от проекта. Это может быть и 0% и ~ 40%. Именно это улучшение достигается за счет внедрения BDD. Это можно внедрить и не называя это словом BDD, но в BDD оно есть. Наличие возможности обойти проблемы является важной часть обеспечения качества.

    В завершение хотелось бы отметить что данная схема разработки также интегрирована у нас с Continuous Integration и разработанной в нашей компании системой тест менеджмента — QA Lens. В QA Lens можно писать те же сценарии что и в iDea использую предметно ориентированный язык. Этот язык состоит из ранее составленного глоссария доступных действий, которые ранее имплементированы.. При выполнении автотеста на Gauge с машины разработчика или CI — в QA Lens автоматически отмечается: какие шаги сценариев были пройдены, а какие нет. Таким образом, прогнав автотест сценария, написанного аналитиком, отдел тестирования сразу получает полную актуальную информацию о состоянии продукта.

    Авторы:

    И. Сунагатов и Ю. Юшкова

    Jmix - это open-source платфора быстрой разработки бизнес-приложений на Java