Что такое behavior driven development
Экстремальное программирование, знакомство с Behavior Driven Development и RSpec
Теория
Для начала, давайте разберемся, что же такое Behavior Driven Development(в дальнейшем BDD) и чем данная техника отличается от Test-Driven Development(в дальнейшем TDD)
Разрабо́тка че́рез тести́рование (англ. test-driven development) — техника программирования, при которой модульные тесты для программы или её фрагмента пишутся до самой программы (англ. test-first development) и, по существу, управляют её разработкой. Является одной из основных практик экстремального программирования.
Хотя подход с предварительным тестированием работает у многих, он подходит не для всех. На каждого разработчика приложений, с успехом применяющего TDD, найдется несколько разработчиков, активно отрицающих этот подход. Несмотря на многочисленность инфраструктур тестирования, таких как TestNG, Selenium и FEST, все равно находится много причин не выполнять тестирование кода.
Две обычные причины отказа от TDD – «у нас недостаточно времени для тестирования» и «код слишком сложный и трудно проверяемый». Другой преградой для программирования с предварительным написанием тестов является сама концепция «тест пишется до кода». Большинство рассматривает тестирование как осязаемое действие, скорее конкретное, нежели абстрактное. Опыт подсказывает, что невозможно проверить то, что еще не существует. Для некоторых разработчиков, остающихся в рамках этой концепции, идея предварительного тестирования — просто оксюморон.
Но что если вместо того, чтобы думать в терминах написания тестов и тестирования компонентов, начать думать о функциональности? Говоря про функциональность, я имею в виду как приложение должно вести себя, фактически его спецификацию.
На самом деле большинство из нас уже и так думает подобным образом. Смотрите:
Линда: Это структура данных, хранящая объекты в порядке «первым вошел, последним вышел» или «последним вошел, первым вышел». Обычно у этой структуры есть API с такими методами, как push() и pop(). Иногда присутствует метод peek().
Фрэнк: Что делает метод push()?
Линда: Метод push() принимает входной объект, например, foo и помещает его во внутренний контейнер, например, массив. Метод push() обычно ничего не возвращает.
Фрэнк: Что будет, если передать методу push() два объекта, например, сначала foo, а потом bar?
Линда: Второй объект bar должен оказаться наверху концептуального стека, содержащего по крайней мере два объекта, так что при вызове метода pop() объект bar должен быть извлечен первым, до первого объекта foo. Если метод pop() вызвать еще раз, должен быть возвращен объект foo и стек должен стать пустым (предполагается, что в нем ничего не было до того, как мы добавили эти два объекта).
Фрэнк: Так метод pop() удаляет самый последний элемент, добавленный в стек?
Линда: Да, метод pop() должен удалить верхний элемент, при этом предполагается, что в стеке есть элементы, чтобы их удалять. Метод peek() работает точно также, но при этом объект не удаляется. Метод peek() должен оставить верхний элемент в стеке.
Фрэнк: Что будет, если вызвать метод pop(), когда в стек еще ничего не было добавлено?
Линда: Метод pop() должен выдать исключение, показывающее, что в стек еще ничего не добавлялось.
Фрэнк: Что будет, если выполнить команду push() null?
Линда: Стек должен выдать исключение, так как null не является допустимым значением для метода push().
Можно ли выделить что-нибудь особенное в этом диалоге, кроме того, что Фрэнк не силен в структурах данных? Нигде не использовалось слово «тестирование». Однако слово «должен» проскальзывало регулярно и звучало довольно естественно.
В подходе BDD нет ничего нового или революционного. Это просто эволюционное ответвление подхода TDD, где слово «тест» заменено словом «должен». Если отложить в сторону слова, то многие найдут понятие «должен» более естественным для процесса разработки, чем понятие «тест». Мышление в терминах функциональности (того, что код должен делать), приводит к подходу, когда сначала пишутся классы для проверки спецификации, которые, в свою очередь, могут оказаться очень эффективным инструментом реализации.
Практика
RSpec — это BDD framework для Ruby
git clone git://github.com/dchelimsky/rspec.git
cd rspec
rake gem
rake install_gem
describe Account, » when first created» do
before do
@account = Account. new
end
after do
@account = nil
end
describe Thing do
before(:all) do
# This is run once and only once, before all of the examples
# and before any before(:each) blocks.
end
before(:each) do
# This is run before each example.
end
before do
# :each is the default, so this is the same as before(:each)
end
it «should do stuff» do
.
end
it «should do more stuff» do
.
end
after(:each) do
# this is before each example
end
after do
# :each is the default, so this is the same as after(:each)
end
after(:all) do
# this is run once and only once after all of the examples
# and after any after(:each) blocks
end
В заключение
Если статья получит положительные отзывы, то появиться цикл статей на данную тему.
Для затравки, ниже несколько ссылок для углубленного, самостоятельного изучения:
Тестирование с использованием BDD
Введение
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», <
>
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
>
>
Вот как выглядит результат теста:
Тут «вылазит» первый недостаток easyb. Дело в том, что непонятно откуда взялось 2 сценария, в то время как описан 1. Если вглядеться в секцию where сценария, то можно увидеть, что подготавливается 2 набора входных и ожидаемых значений. К сожалению конструкция where не документирована даже на сайте проекта, по крайней мере я её там не нашел.
Ниже приведен пример упавшего теста-сценария
Spock
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
Кстати, сценарии в 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
Тут нужно соблюдать Naming Conventions как в именах файлов сценария и реализации, так и в именах методов реализации и шагов сценария. Иными словами они должны соответствовать. Соответствие достигается путем использования аннотаций Given, @When, Then и строк регулярных выражений в качестве аргументов к аннотациям.
Используя группы регулярных выражений можно выделять аргументы методов реализации.
Ниже приведен пример прошедшего теста на cucumber
А вот пример упавшего feature
Мне по-душе разделение сценария от его реализации. Кого то может смутить использование регулярных выражений чтобы «увязать» реализацию со сценарием, однако они скрыты от пишущего сценарий, и большинство разработчиков знакомы с ними, так что этот факт я бы не стал относить к недостаткам. За информацией о cuke4duke — реализации для Java прошу зайти сюда.
BDD — рабочий метод или TDD в модной обертке?
Два подхода к разработке через тестирование вызывают особенно много споров — из-за некоторого методологического сходства TDD (Test Driven Development) и BDD (Behaviour Driven Development) часто путают даже профессионалы. Старшие инженеры по автоматизации тестирования «Альфа-Лаборатории» Юлия Ковалева и Анна Чернышева рассказывают базовые вещи о сходстве и различиях двух популярных методик и то, какой подход у них используется в самой компании.
Юлия Ковалева, старший Java-разработчик автотестов в «Альфа-Лаборатории»
Посвятила тестированию более 4 лет, сейчас разрабатывает библиотеку шагов для масштабирования автоматизации тестирования с использованием Cucumber и Selenide.
Анна Чернышева, старший Java-разработчик автотестов в «Альфа-Лаборатории»
Работала в крупных e-commerce проектах, участвует в создании и поддержке нескольких BDD-фреймворков, а также занимается внедрением инженерных и DevOps-практик.
— В чем заключается основное различие методик TDD и BDD?
Анна Чернышева: Понимание методик TDD и BDD отличается в разных компаниях, мы расскажем о том, как все устроено в «Альфа-Лаборатории». Концепции обоих подходов похожи, сначала идут тесты и только потом начинается разработка, но предназначение у них совершенно разное. TDD — это больше о программировании и тестировании на уровне технической реализации продукта, когда тесты создают сами разработчики. BDD предполагает описание тестировщиком или аналитиком пользовательских сценариев на естественном языке — если можно так выразиться, на языке бизнеса.
— BDD — просто модное слово или принципиально новый подход к разработке через тестирование? От TDD его отличает только использование естественных языков для описания тестов?
Юлия Ковалева: BDD и в самом деле модное слово, но далеко не все умеют его правильно «готовить». В «Альфа-Лаборатории» нам пришлось комплексно подойти к решению задач и полностью изменить многие аспекты функционирования всей команды, что позволило существенно удешевить процесс тестирования. Нанять умеющего описывать тестовые сценарии на русском языке человека намного проще, чем найти специалиста, способного реализовать эти тесты, например, на Java.
BDD подход совместно с инженерными практиками позволил нам отказаться от legacy-документации, содержащей неактуальную информацию, и получать новую документацию налету, хранить ее вместе с проектом, что позволило приблизить аналитиков и тестировщиков к коду.
— В разработку сценариев BDD вовлечены не только тестировщики, необходимость в автоматизаторах сохраняется?
Юлия Ковалева: Разумеется, оставить в команде только тестировщиков без автоматизаторов — достаточно недальновидно. Потребуется некий инструментарий и владеющий им человек, чтобы реализовать ту техническую составляющую, которую тестировщик самостоятельно сделать не сможет.
Анна Чернышева: Потребность команды в количестве экспертов по автоматизации тестирования уменьшается, поскольку есть фреймворки, которыми могут пользоваться тестировщики. Автоматизатор добавляет новую функциональность в тестовый фреймворк, и она сразу становится доступна всем командам.
— Помимо традиционных для TDD unit-тестов при использовании BDD-подхода проводятся также behavior-тесты. На этом различия процессов тестирования заканчиваются или дело обстоит сложнее?
Анна Чернышева: Все гораздо сложнее, поскольку BDD — скорее процесс, целью которого является удешевление реализации новых фич. Еще на старте разработки мы получаем важные артефакты. Например, понятную для поддержки документацию. Эта документация дает возможность всем заинтересованным лицам сформировать свое представление о продукте и сценариях пользовательского поведения, которые должны быть реализованы в ходе итераций разработки. С BDD-подходом мы также снижаем порог входа в проект новых участников.
— Многие считают, что BDD можно рассматривать как переход от основанной на unit-тестах разработки к разработке, основанной на интеграционном тестировании. А как ситуация обстоит на самом деле?
Юлия Ковалева: Мы провели такой эксперимент. Попытка использовать BDD-инструменты для написания unit-тестов успехом не увенчалась, через какое-то время разработчик отказался писать поведенческие тесты. Когда мы говорим про автоматизацию, BDD идеально вливается в эту историю, но применять такой подход к модульному тестированию не стоило, пользы нам это не принесло. При интеграционном тестировании польза от BDD будет значительной — здесь мы смотрим на весь продукт в целом.
— В каких случаях для тестирования применяется BDD подход и чем он лучше более традиционного TDD?
Анна Чернышева: BDD в основном используется для проверки взаимодействия разных компонентов системы, это, как уже было сказано, уровень интеграционного тестирования — оно поведенческое и проверяет различные бизнес-кейсы. TDD используется для разработки через модульное тестирование непосредственно программистами, которые пишут код через этот подход. В общем, если по-простому, TDD проверяет исключительно модули, а BDD — пользовательские сценарии.
— Зачем потребовались новые BDD-фреймворки, не проще оформить BDD в набор рекомендаций по написанию TDD и использовать существующие?
Юлия Ковалева: BDD появился, чтобы сделать команду разработки ближе к бизнесу, организовать диалог между бизнесом, разработчиками и тестировщиками. При TDD-подходе нужно писать тесты на формальных языках программирования. Тесты и код лежат в одном месте, их достаточно сложно поддерживать, а уж тестировщик или бизнес-аналитик едва ли полезет смотреть, что мы там написали. Два наиболее популярных BDD фреймворка — JBehave и Cucumber. Как их правильно применять, мы расскажем на ближайшей конференции Гейзенбаг 2017 Piter.
Анна Чернышева: Эта путаница между TDD и BDD пошла от общей для разных подходов идеи: сначала пишутся тесты, а потом идет разработка. При всех сходствах они предназначены для разных целей и требуют применения различных инструментов.
— То есть BDD нельзя считать расширением TDD?
Анна Чернышева: Да, мы считаем именно так. Вместо тестовой модели у нас есть пользовательские сценарии, которые мы автоматизируем в одном спринте с разработкой. Более того, в «Альфа-Лаборатории» BDD-подход органично внедрен и в инженерные практики.
— QA считают женской профессией. Как вы полагаете, с чем это связано и насколько соответствует действительности? Есть ли вообще в IT понятие женской и мужской профессии?
Анна Чернышева: Сейчас от этого стараются уйти, но женское мышление более абстрактно, а мужчинам важны конкретные детали — это, конечно, все индивидуально, но если брать «Альфа-Лабораторию», то девушек-тестировщиков в процентном соотношении несколько больше. Может, тестирование требует творческого подхода, а девушки — более творческие натуры?
Юлия Ковалева: Можно провести эксперимент и подсчитать, сколько девушек и мужчин придут на Гейзенбаг.
На Гейзенбаг 2017 Юлия Ковалева и Анна Чернышева сравнят Cucumber и JBehave. У какого BDD-фреймворка больше возможностей? Как их правильно «готовить» и с какими трудностями придется столкнуться во время тестирования — состязание ведущих разработчиков автотестов «Альфа-Лаборатории» пройдет в лучших традициях Mortal Kombat.
Полная программа конференции доступна на сайте.
Введение в программирование через поведение (BDD)
История: Эта статья впервые появилась в журнале Better Software в марте 2006. Она была переведена на несколько языков.
Однажды я столкнулся с проблемой. Обучая разработчиков практикам agile программирования, таким как TDD в различных проектах, я часто встречал непонимание и растерянность. Они хотели знать, где начать, что тестировать, а что не тестировать, как много тестировать за раз, как называть тесты и как понять, почему тесты падают.
Чем больше я пользовался TDD, тем больше я понимал, что не столько оттачиваю своё мастерство, достигая новых его вершин, сколько то, что это было движение в слепую. Я помню, как мне все чаще приходила мысль: «Эх, вот бы мне кто-нибудь сказал это раньше!», чем мысль: «Отлично, дорога ясна». Я решил, что нужно найти способ обучать TDD, показывающий, как верно работать с ним сразу и без ошибок.
И этот способ — это программирование через поведение. Оно выросло из выработанных agile практик и призвано сделать их доступнее и эффективнее для команд, незнакомых с ними. Со временем, BDD стало включать в себя agile анализ и автоматическое приемочное (прим. acceptance) тестирование.
Выражайте названия тестов (методов) предложениями
Моё открытие, моё радостное «Ага!» я почувствовал, когда мне показали обманчиво простую утилиту agiledox, написанную моим коллегой, Крисом Стивенсоном. Она берёт класс с JUnit тестами и печатает названия их в виде простых предложений. Так тестируемый случай, выглядевший как:
печатается как-то так:
(Прим. «CustomerLookup [поиск заказчика]: находит заказчика по ID; падает, если заказчики повторяются»)
Слово «test» убрано из названия класса и из методов, и camel запись имен преобразована в обычный текст. Это все, что она делает, но эффект поразительный.
Разработчики поняли, что так они могут создавать, по крайней мере, часть документации для себя и они стали это делать, записывать названия тестов в виде предложений. Более того, они обнаружили, что то, что, когда они записывали имена методов, используя лексику бизнес домена, созданная документация была понятна бизнес пользователям, аналитикам и тестировщикам.
Не ошибайтесь, используйте этот простой шаблон предложения для тестов
Потом я нашел интересный шаблон предложения, по которому все имена тестов следует начинать со слова будет (прим. should). Это предложение — This class should do something (прим. Этот класс будет делать что-то) — предполагает, что вы можете написать тест только для текущего класса. Так вы не ошибетесь. Если вы обнаружите, что пытаетесь написать тест, который нельзя так выразить, то, видимо, это поведение другого объекта.
К примеру, однажды, я пишу класс, проверяющий ввод с экрана. Большинство полей — это обычные детали клиента: имя, фамилия и другое; но потом там оказались поле для даты рождения и другое для возраста. Я начал писать, `ClientDetailsValidatorTest` (прим. валидатор деталей клиента) с такими методами, как `testShouldFailForMissingSurname` (прим. тест будет падать, если нет фамилии) и `testShouldFailForMissingTitle` (прим. тест будет падать, если нет заглавия).
Потом я заморочился с вычислением возраста и погряз в мире запутанных бизнес правил: Что если есть дата рождения и возраст и они не совпадают? Что если день рождения сегодня? Какой возраст, если у меня, есть только дата рождения? Я начал писать тогда длинные названия тестов для описания этого поведения, но остановился и решил переместить все это. Поэтому я создал класс, названный `AgeCalculator` (прим. калькулятор возраста) со своим `AgeCalculatorTest` (прим. тест для калькулятора возраста). Так все поведение объекта для вычисления возраста переместилось в этот калькулятор и тогда `ClientDetailsValidator`у нужен был только один связанный тест, — проверка верного взаимодействия с калькулятором возраста.
Если какой-то класс делает, что-то кроме одного, то это обычно признак того, что я должен насоздавать еще классов для этого другого. Я создаю, новый сервис в виде интерфейса, описывающего, что он делает, и передаю его конструктору первого класса:
Такой стиль создания объектов вместе, известный как внедрение зависимости, особенно полезен вместе с mock-объектами.
Именуйте тесты ясно: поможет, когда они упадут
Спустя некоторое время, я обнаружил, что, изменяя код и ломая тесты, я мог ясно понять по их названиям предполагаемое поведение кода. Обыкновенно, происходило одно из трех:
Менее различимый оттенок слова будет (прим. should) становиться понятным, когда сравниваешь его с более формальной альтернативой должен (прим. will или shall). Будет подразумевает, что вы можете сомневаться в тестируемом поведении: «Действительно ли он это будет делать?» Это позволяет легче отличить ситуацию, когда тест действительно падает из-за сделанной в коде ошибки, от той, когда ваши представления о поведении системы уже неверны.
Используйте слово «поведение», а не «тест»
Итак, теперь у меня был тот инструмент — agiledox — для того, чтобы убрать слово «тест» и использовать то предложение для каждого наименования теста. Тут я понял, что люди, изучая TDD, почти всегда спотыкаются о слово «тест».
Конечно, неверно, что тестирование не присуще TDD: итоговый набор методов — это хороший способ проверки работоспособности кода. Однако, если тесты неполно описывают поведение вашей системы, то они обманывают вас ложным чувством безопасности.
Я стал использовать слово «поведение» вместо слова «тест», когда работал с TDD, и обнаружил, что оно по всей видимости не только подходило, но и магическим образом отпадали все вопросы у учеников. Теперь у меня были ответы на некоторые из тех TDD вопросов. Как проще назвать ваш тест? — это предложение описывающее следующее интересное вам поведение. Вопрос как детально тестировать? становиться чисто теоретическим: вы можете описать только столько поведения, сколько позволяет предложение. Что делать, когда тест падает? — просто следуйте выше описанным шагам: либо вы сделали баг, либо поведение переместилось, либо тест больше не нужен.
Я обнаружил, что думать поведениями, а не тестами, настолько выгодно, что я стал называть TDD тестированием через поведение или BDD.
JBehave акцентируется на поведении, а не на тестировании
Под конец 2003 года, я решил, что пришло время вложить деньги — ну или хотя бы мое время — в то, о чем я говорил. Я начал писать то, что должно было заменить JUnit — JBehave. В нем не было отсылки к тестированию, он замещал это лексикой, построенной вокруг проверяемого поведения. Все это для того, чтобы узнать как будет эволюционировать такой фреймворк, если я буду строго держаться за мои новые мантры о тестировании через поведение. Я также думал, что этот инструмент будет полезен для обучения TDD и BDD без отвлечений на слова производные от слова тест.
Чтобы определить поведение для гипотетического `CustomerLookup` (прим. поиск заказчика) класса, я бы написал класс для поведения, названный, к примеру, `CustomerLookupBehavior` (прим. поведение поиска заказчика). Он бы содержал методы, которые начинались бы со слова «будет» (прим. should). Программа запускающая проверку поведения (прим. behaviour runner) тогда создавала бы этот класс поведения и вызывала бы каждый из методов, описывающих поведение по очереди, так же, как это делает JUnit для тестов. Она должна была бы потом отчитываться о прогрессе по ходу исполнения и выдавать итог в конце.
Моя первая цель была сделать так, чтобы JBehave проверял сам себя. Я добавил поведение, которое позволяло этой программе запускать себя. У меня получилось перенести все JUnit тесты в JBehave поведения и получить ту же обратную связь, что и с JUnit.
Определите следующее самое важное поведение
Вскоре после этих экспериментов с JBehave, я стал понимать концепцию бизнес значимости (прим. business value). Конечно, я всегда знал, что я пишу код для чего-то, но я никогда не думал о значимости кода, который я писал сейчас. Мой коллега, бизнес аналитик Крис Маттс, подтолкнул меня к размышлениям о бизнес значимости в контексте тестирования через поведение.
Имея вот эту цель — сделать JBehave самопроверяющим, я обнаружил, что для того, чтобы легче сосредотачиваться, нужно спрашивать себя: «Какая следующая самая важная вещь, которую система не делает?»
Этот вопрос потребует от вас определить значимость тех фич, которые вы еще не реализовали и расставить приоритеты для них. Также это поможет вам сформулировать имя для метода описывающего поведение: система не делает X (где X какое-то ясное поведение), и X важно; что означает, что система будет делать X, поэтому ваш следующий метод поведения вот такой:
Вот теперь у меня есть ответ на тот вопрос о TDD, а именно, где начать.
Требования — это тоже поведение
Так у меня в руках оказался фреймворк, который помогал мне понимать и, что важнее, объяснять, как работает TDD и подход, который избавлял меня от всех тех подводных камней, которые я обнаруживал.
Ближе к концу 2004 года я как-то рассказывал открытый мной подход, основанный на поведении, Маттсу, и он сказал: «Но это в точности, как анализ». И после некоторой паузы для обдумывания, мы решили применить все это основанное на поведении мышление для определения требований. Так, если бы мы смогли разработать удобную лексику для аналитиков, тестировщиков, разработчиков и для бизнеса, то мы были бы на верном пути устранения неопределенности и взаимонепонимания, которые появляются, когда техники разговаривают с людьми из бизнеса.
BDD дает «доступный всем язык» для анализа
Где-то в это время Эрик Эванс опубликовал свой бестселлер, книгу «Предметно-ориентированное проектирование» (прим. Domain-Driven Design by Eric Evans). В ней он описывает концепцию моделирования системы, использующую доступный всем язык, основанный на бизнес модели, так чтобы бизнес лексика проникала прямо в код.
Мы с Крисом поняли, что мы пытаемся определить доступный всем язык для самого процесса анализа! У нас был хороший старт. В общем доступе в нашей компании уже был шаблон для пользовательских историй, который выглядел так:
(прим. Будучи X, я хочу Y, так, что произойдет Z.)
где Y — какая-то фича, Z — польза или значение этой фичи и X — человек (или роль), получающий пользу. Преимущество этого предложения в том, что он заставляет вас определить значение разрабатываемой истории во время первого определения ее. Ведь бывает, что когда нет реального бизнес значения истории, то происходит какая-то деградация до чего-то такого: «… Я хочу [какую-то фичу], ну и поэтому [я просто сделаю, да и все, хорошо?].» Наш метод позволяет вынести за рамки проверки эти довольно эзотерические требования.
И с этого старта мы с Маттсом были на пути открытия, того что каждый agile тестировщик и так знает: поведение в пользовательской истории — это просто критерий принятия, а именно, если система выполняет все критерии принятия, то ее поведение верно; если нет — то нет. Поэтому мы создали шаблон предложения для записи критерия оценки пользовательской истории.
Этот шаблон должен был быть настолько простым, чтобы аналитик не ощущал бы ограничений и неестественности, но и упорядоченным настолько, что можно было бы поделить историю на составляющие фрагменты и автоматизировать их. Поэтому мы описали критерий принятия, используя понятие сценарий, который принимал следующую форму:
Имея (прим. given — данное) какой-то контекст,
Когда (прим. when) происходит событие,
Тогда (прим. then) проверить результат.
+Название: Клиент снимает наличные+
Являясь клиентом,
Я хочу снять деньги в банкомате,
Чтобы мне не ждать в очереди в банке.
Ну, а как мы поймем, что история завершена? У нас несколько сценариев: на счету есть деньги; на счету нет денег, но можно снять в пределах овердрафта; счет превысил овердрафт. Конечно, будут другие сценарии: счет окажется в овердрафте именно с этим снятием, или у банкомата нет денег.
Используя Имея-Когда-Тогда шаблон, первые два сценария могут выглядеть так:
+Сценарий 1: На счету есть деньги+
Имея счет с деньгами
И валидную карточку
И банкомат с наличными
Когда клиент запрашивает наличные
Тогда убедиться, что со счета было списание
И убедиться, что наличные выданы
И убедиться, что карточка возвращена
+Сценарий 2: Снятие со счета превышает овердрафт+
Имея счет с превышением лимита
И валидную карточку
Когда клиент запрашивает наличные
Тогда убедиться, что сообщение об отказе показано
И убедиться, что наличные не выданы
И убедиться, что карточка возвращена
Оба сценария основаны на одном и том же событии и даже имеют несколько общих исходных условий и результатов. Мы можем извлечь из этого выгоду, используя заново исходные условия, события и результаты.
Сделайте критерий принятий выполняемым
Фрагменты этого сценария — исходные условия, событие и результаты — достаточно малы, чтобы быть запрограммированными. У JBehave есть объектная модель, позволяющая явно соотнести фрагменты сценария с Java классами.
Вы пишите класс, представляющий каждое исходное условие (прим. given) так:
и один для того события так:
и так далее для результатов сценария. JBehave затем связывает это все вместе и выполняет. Он создает «мир», который где-то существует для хранения ваших объектов, затем JBehave передает его каждому исходному условию (прим. given) по очереди так, что они могут инициализировать мир каким-то известным состоянием. JBehave, затем, просит событие «случиться» в этом мире, которое выполняет заявленное поведение конкретного сценария. И, наконец, JBehave передает управление любому результату, определенному нами в конкретной истории.
Имея классы, представляющие каждый фрагмент сценария, мы можем использовать заново фрагменты для других сценариев или историй. Сначала, эти фрагменты реализованы, используя mock-объекты для установки на счету денег или задания карточке валидности — так мы создаем фундамент для реализации поведения. По мере того, как вы реализуете конкретное приложение, исходные условия и результаты изменяются и начинают использовать реальные классы, созданные вами, и таким образом, к моменту когда сценарий закончен, они становятся верными функциональными тестами от начала до конца.
Настоящее и будущее BDD
После некоторой паузы, JBehave снова активно разрабатывается. Его ядро достаточно закончено и надежно. Следующий шаг — это интеграция с популярными Java IDE такими как IntelliJ IDEA и Eclipse.
Дейв Астель активно продвигал BDD последнее время. Его блог и различные опубликованные статьи спровоцировали шквал активности. Самая заметная — это проект rspec для создания BDD фреймворка на языке Ruby. Я начал работу над rbehave, который будет реализацией JBehave на Ruby.
Мои коллеги, после использования BDD техник в различных реальных проектах, сообщали, что этот способ имеет огромный успех. JBehave подпрограмма для запуска историй — та часть, что проверяет критерий принятия — активно разрабатывается.
В будущем мы хотим иметь такой редактор полного круга, который позволял бы бизнес аналитикам и тестировщикам записывать истории в обычном текстовом редакторе, который бы создавал stub-объекты для классов поведения, и все это на языке бизнес модели. BDD эволюционировал с помощью многих людей и я выражаю огромную благодарность им.
Прим. Дэн Норт — преподаватель agile методологий разработки. Разрабатывает ПО и учит этому около 20 лет. Создатель собственного агентства по консультированию и разработке ПО. Ввел понятие разработка через поведение (BDD).