Пользователь
0,0
рейтинг
10 марта 2012 в 01:16

Разработка → Тестирование с использованием BDD

Введение


Современные проекты все чаще предъявляют высокие требования к покрытию автоматическими тестами. В наше время писать тесты не просто признак хорошего тона, но одно из требований, которое предъявляется к коду. Все чаще мы слышим такие аббревиатуры, как TDD (Test Driven Development) и BDD (Behaviour Driven Development) и многие строго следуют этим подходам в разработке.
BDD это одна из разновидностей TDD, и об этом я хотел бы написать в этой статье. Точнее не о самом BDD, а о frameworks, которые нам предоставляет индустрия на сегодняшний день. А если уж быть совсем точным, то о трех из них: spock, easyb и cucumber.

TDD и BDD

Я не буду тут ссылаться на статьи и презентации корифеев IT индустрии. Мне запомнилась одна фраза из Twitter по поводу TDD которая засела в моем сознании, и которая на мой взгляд четко и коротко характеризует TDD подход. К сожалению дословно я её привести не могу, но смысл в ней следующий: «если вы следуете TDD, то можете быть 100% уверены, что каждая строчка кода была написана благодаря упавшему(ым) тесту(ам)». Я видел и слышал много дебатов по поводу достоинств и недостатков TDD и BDD, но а) тесты писать надо б) если код был написан благодаря упавшему тесту, то этому коду можно доверять, и с легкостью его изменять (рефакторить) не боясь испортить поведение системы.
Теперь про BDD. Появилось это явление позже и как утверждает Фаулер в статье «Mocks Aren't Stubs» благодаря так называемым мокистам. С другой стороны этот подход активно продвигают ребята из Agaile тусовки, сводя к минимуму расстояние между разработчиками, пользователями и аналитиками систем. Достигается это путем получения Executable Scenarios, иными словами, сценарии которые описывают пользователи переводятся в исполняемый тест. BDD frameworks с этим удачно справляются.
Теперь перейдем к сравнению.
Все примеры описывают один и тот же сценарий. Я опущу описание проблемы решение, которой необходимо покрыть тестами, потому как сами сценарии должны ясно описать её.
Автор статьи приводит реализации BDD в порядке возрастания симпатии к ним.

Easyb

Данный framework написан на Groovy. Как и все BDD реализации поддерживает нотацию Given-When-Then. Легко интегрируется в Continuous Integration (CI).
Вот пример сценария:
description "This story is about sqrt optimisation algorithm"
narrative "this shows sqrt optimisation", {
as a "java developer"
i want "to know how sqrt optimisation works"
so that "that I can pass google interview"
}

before "init input and expected result",{
}

where "complete scenarios data",{
input = [[5, 10, -3, 17, 12, 1, -2, 13, -12], [5, 8, 13, 5, 21, 6, 3, 7, -2, 4, 8, 12]]
leftIndex = [2,3]
rightIndex = [5,10]
expectedSumm = [27,51]
}

scenario "find summ within two indexes #leftIndex and #rightIndex of the array #input",{
given "An Sqrt algorithm implementation",{
alg = new SqrtDecompositionSum(input.toArray(new int[0]))
}

when "calc sum between two indexes", {
actualSum = alg.calcSummBetween(leftIndex, rightIndex)
}

then "summ should be equal expected #expectedSumm", {
actualSum.shouldBe expectedSumm
}
}

Вот как выглядит результат теста:
image

Тут «вылазит» первый недостаток easyb. Дело в том, что непонятно откуда взялось 2 сценария, в то время как описан 1. Если вглядеться в секцию where сценария, то можно увидеть, что подготавливается 2 набора входных и ожидаемых значений. К сожалению конструкция where не документирована даже на сайте проекта, по крайней мере я её там не нашел.
Ниже приведен пример упавшего теста-сценария


Как видим результат вполне читаем. Обратите внимание на строку actualSum.shouldBe expectedSumm. Это sugar, который предоставляет easyb для проверки ожидаемого с актуальным результатом.
Для того чтобы запустить сценарий из IDE необходимо поставить easyb plugin.
Вторым недостатком я могу отметить то, что последний раз обновления easyb было в 2010 году, что на мой взгляд уже достаточно давно.
За подробностями обращайтесь на сайт проекта.

Spock

Spock, как и EasyB выходец из groovy. Его очень любят использовать разработчики Groovy/Grails. Наш сценарий будет выглядеть так:
class SqrtSumAlgSpecTest extends Specification {
Algorithm alg
def "Sqrt sums scenarios"(){
when:
alg = new SqrtDecompositionSum(input.toArray(new int[0]))
then:
outputSumm == alg.calcSummBetween(leftIndex, rightIndex)
where:
input | leftIndex | rightIndex | outputSumm
[5, 10, -3, 17, 12, 1, -2, 13, -12] |2 |5 |27
[5, 8, 13, 5, 21, 6, 3, 7, -2, 4, 8, 12] |3 |10 |52
}
}


Spock мне больше нравится конструкцией where. Для того, чтобы создать spock спецификацию, необходимо создать groovy класс унаследованный от spock.lang.Specification.
Ниже приведен пример упавшего теста-сценария:


Spock, на мой взгляд, ближе к разработчику нежели к аналитику или QA инженеру, однако все равно легко читаем.

Cucumber

С Cucumber я познакомился совсем недавно, и чем больше я с ним экспериментировал, тем больше он мне нравился. В отличие от первых двух, Cucumber выходец из Ruby. Существует его реализация для Java и С#.
Сценарии на cucumber состоят из двух файлов: собственно сценарий, и его реализация на Java, C#, Ruby. Это позволяет отделить сценарий от реализации, что делает сценарии абсолютно обычным повествованием на английском языке, приведем пример

Feature: Sqrt Sums Algorithm Feature
In order to ensure that my algorithm works
As a Developer
I want to run a quick Cuke4Duke test

Scenario Outline: Sqrt Sums Alg Scenario
Given The input array <input array>
When The calc sum between <Left index>, <Right index>
Then The summ is <output summ>.

Examples:
|input array |Left index |Right index|output summ|
|5, 10, -3, 17, 12, 1, -2, 13, -12 |2 |5 |27 |
|5, 8, 13, 5, 21, 6, 3, 7, -2, 4, 8, 12 |3 |10 |52


Кстати, сценарии в cucumber называют features.
А вот реализация

public class SqrtsumsalgFeature {
private Algorithm alg;
private int result;

@Given ("^The input array ([\\d\\s\\-\\,]*)$")
public void theInputArray(String input) {
String[] split = input.split(",");
int[] arrayInput = new int[split.length];
for (int i = 0; i < arrayInput.length; i++) {
arrayInput[i] = Integer.valueOf(split[i].trim());
}
alg = new SqrtDecompositionSum(arrayInput);
}

@When ("^The calc sum between ([\\d]*), ([\\d]*)$")
public void theCalcSumBetween(int L, int R) {
result = alg.calcSummBetween(L, R);

}

@Then ("^The summ is ([\\d]*).$")
public void theSummIs(int expectedResult) {
Assert.assertThat(result, is(expectedResult));
}
}


Тут нужно соблюдать Naming Conventions как в именах файлов сценария и реализации, так и в именах методов реализации и шагов сценария. Иными словами они должны соответствовать. Соответствие достигается путем использования аннотаций Given, @When, Then и строк регулярных выражений в качестве аргументов к аннотациям.
Используя группы регулярных выражений можно выделять аргументы методов реализации.

Ниже приведен пример прошедшего теста на cucumber



А вот пример упавшего feature



Мне по-душе разделение сценария от его реализации. Кого то может смутить использование регулярных выражений чтобы «увязать» реализацию со сценарием, однако они скрыты от пишущего сценарий, и большинство разработчиков знакомы с ними, так что этот факт я бы не стал относить к недостаткам. За информацией о cuke4duke — реализации для Java прошу зайти сюда.

Итог


Статья получилась выше среднего. Хотелось бы еще описать интеграцию с maven и Continuous Integration. Думаю, что это будет тема будущего поста.
Пишите исполняемые сценарии, это не только полезно, но и доставляет удовольствие.
@semenodm
карма
3,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (18)

  • +11
    Относительно TDD. Под любой TDD может быть написан полностью ему соответствующий, но не являющийся осмысленным код. Исключение — если тесты покрывают 100% множества входных данных, что, мягко говоря, не есть быть возможно в реальных ситуациях. Таким образом, достаточно сделать «обратную компиляцию» тестов, чтобы получить фигню, которая всем хороша, только не работает.
    • +8
      Под любой пост может быть написан полностью ему соответствующий, но не являющийся осмысленным комментарий.
    • +2
      Вы вообще не пишете тесты?
      • +1
        С учётом тех программ, которые я «пишу» — нет, тестов не пишу. Наши программисты — пишут.
        • 0
          Тогда я не пойму почему вы критикуете данный подход.
          А «подгоном» для галочки, можно много чего сделать.
          • 0
            Я его не критикую в смысле «отвергаю», я скорее ставлю под сомнение тезис agile о том, что можно творить что угодно в коде, пока он проходит тесты. До определённой степени это так, но принимать за максиму я бы не стал.
            • 0
              Да, нельзя быть ни в чем быть на 100% уверенным. Все упирается в качество автоматизированных тестов. Заметьте не только степень покрытия, Unit тестами, кода, но и Acceptance тесты. Когда вы пишите Unit тест, вы подтверждаете правильность реализации с точки зрения разработчика, Acceptance тесты подтверждают то, что вы реализовали именно то что хотел бизнес. Иными словами Unit/Integration тесты для разработчиков, Aceeptance для всех остальных.
              Поверьте, если у вас разумно написаны тесты всех уровней пирамиды тестирования, то творить с кодом все, что угодно они вам просто не позволят.
  • –2
    1) Сценарий — это сценрий, фича — это фича. Давайте не путать понятия
    2) «Тестирование с использованием BDD» равносильно фразе «Вождение машины с помощью дороги».
    • 0
      2) Все относительно, например: поезд является машиной, рельсы — дорогой. А съехать поезд с рельсов не может (граничные случаи не рассматриваем)
      • 0
        Я не к тому. Не BDD используется в тестах, а тесты в BDD.
  • 0
    Материал очень полезен, но мне не понравилось оформление:
    1) Примеры кода можно оформить в тегом «код», а то сейчас оно выглядит как будто автор применил тег «цитата»
    2) Поясняющий текст к картинке слишком далеко расположен от картинки, которую он поясняет.

    ЗЫ: Сорри за занудство! ;)
    • 0
      Со своей стороны прошу прощения за оформление. Да действительно под конец написания статьи, я заметил, последняя часть его отображается как код, такое впечатление, нету закрывающегося тэга . Искал-искал — не нашел.
      Тэгом «цитата» я вообще не пользовался. Однако я еще раз проверю текст.
      Второй пункт приму к сведению.
      Спасибо за критику.
    • 0
      Исправил проблему с тэгами, оказывается дело в < > символах в тексте cucumber feature
  • 0
    Мыша на скриншоте порадовала.
  • +1
    Круто, не был в курсе кукумбера для Java и C#.
    Для некомпилируемых фреймворков — t/bdd крайне необходимо, без этого вы получите свой маленький ад.
    • 0
      «некомпилируемых фреймворков», что вы имели в виду? Буду ли я прав если предположу, что вы говорите о динамических языках программирования?
  • 0
    Для тех, кому интересно покопаться в реализации самому, милости прошу на GitHub. Все три BDD frameworks представлены в этом проекте, собственно это полигон для этой статьи. Кроме того, можно поглядеть сюда, чтобы увидеть как работает Spock в связке с Selenium2/webdriver + Geb.
    • 0
      а кто в CnS копаться будет?
      +1 за статью, пиши еще.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.