![](https://habrastorage.org/storage2/e1d/94a/cf0/e1d94acf0ee98891f19c78db248a64d5.png)
На Хабре довольно много упоминаний о BDD. К сожалению, статьи, которые я читал, так и не дали мне ответа на вопрос «а зачем мне все это нужно?» Ответ пришел с неожиданной стороны. Когда я всерьез занялся вопросом автоматизации приемочного тестирования, мне под руку попалась книга Gojko Adzic (не уверен в транскрипции, поэтому не стал переводить имя автора) Specification By Example.
Читая ее, я не уставал удивляться: каждая новая глава описывала шишки, которые я набивал на своем личном опыте, и предлагала решения аналогичные или лучшие, чем те, к которым я приходил сам методом проб и ошибок.
Эта статья – первая в цикле «BDD для прагматиков». В ней описаны ключевые элементы наиболее эффективного, на мой взгляд, процесса разработки коммерческого ПО в современных условиях. Два продолжения будут посвящены работе со SpecFlow и автоматизации приемочного тестирования.
![](https://habrastorage.org/storage2/475/fed/99a/475fed99a79bbf66e772beea6d80e253.png)
Чтобы продукт «взлетел» в условиях современного IT-рынка, нам, разработчикам, нужно достичь две цели. Сделать правильный продукт (именно то, что рынок захочет купить) и сделать продукт правильно (без спагетти-кода, костылей и прочего технического долга).
Правильный продукт и продукт, сделанный правильно, – не одно и то же!
Невыполнение первого условия приведет к финансовому краху, а значит с высокой вероятностью к закрытию проекта. Невыполнение второго – к тому, что в один прекрасный день мы обнаружим себя по пояс в
Specification By Example – это процесс, позволяющий регулярно добиваться выполнения обоих пунктов. В основе процесса лежат agile, tdd, bdd, continuous integration и автоматизация тестирования.
Ключевые элементы Specification By Example:
- Выделяйте главное (deriving scope from goals)
- Составляйте спецификацию совместно (specifying collaboratively)
- Приводите примеры (illustrating using examples)
- Очищайте спецификацию (refining the specification)
- Внедряйте автоматизацию тестирования без изменения спецификации (automating validation without changing specification)
- Встраивайте выполнение тестов в процесс сборки и развивайте документацию (validating frequently, evolving a documentation system)
![](https://habrastorage.org/storage2/00f/0a0/534/00f0a0534e75bcb954a8e05e62acf0ee.png)
Выделяйте главное (deriving scope from goals)
![](https://habrastorage.org/storage2/032/388/2f1/0323882f19bdb98a3273daf98f9cc658.jpg)
Подавляющее меньшинство людей способно сформулировать, что им действительно нужно. В большинстве случаев вы столкнетесь с тем, что клиент сформулирует требования не в виде «у меня есть цель, как мы можем ее достичь?», а: «сделайте мне вундервафлю, как у соседа, но с перламутровыми пуговицами». Клиент будет рассказывать вам, как он видит решение проблемы, и погружаться все глубже в детали реализации.
Здесь кроется первая и самая главная ошибка. Обычно одну и ту же проблему можно решить несколькими способами, а в виде требований нам приходит уже запрос на реализацию одного из возможных решений. Но не все
Наша задача, как инженеров, предложить решение лучше и дешевле, чем просит клиент. Этот принцип сформулировал еще Генри Форд:
«Если бы я спросил людей, чего они хотят, они бы попросили более быструю лошадь».
Истребитель F-16 – один из самых успешных истребителей за всю историю армии США. Первоначальные требования к разработке были достичь скорости Mach 2-2.5, что в комплексе с другими требованиями делало разработку и производство такого самолета очень дорогими. Harry Hillaker – ведущий конструктор F-16 – уточнил, почему так важно это требование скорости, и получил ответ: «истребитель должен смотаться, если станет действительно жарко». Hillaker предложил альтернативное решение и спроектировал истребитель, превосходящий на тот момент другие по маневренности.
Прошло более 30 лет, а эти истребители все еще производят. 4400 самолетов продано в 25 стран мира. Именно на F-16 летают в большинстве голливудских фильмов: от «Дня независимости» до «Трансформеров»… F-16 до сих пор не может развивать скорость выше, чем Mach 2.
F-16 оказался таким успешным, потому что инженер предложил решение лучше и дешевле, чем просил клиент.
Обращайте внимание на цели, а не переходите сразу к решению. Достичь цели можно разными способами.
Как выделить главное
Альберт Эйнштейн однажды сказал:
«Формулирование проблемы зачастую важнее ее решения».
Прежде чем кодировать, составьте спецификацию. Да, мы – разработчики – не любим бумажки, формализм и многостраничные талмуды, которые никто в итоге не читает. Но задумайтесь, как часто вы проклинали того, кто составлял требования и приемочные критерии. Возьмите на себя ответственность и составьте спецификацию сами в том виде, в котором вам будет удобно работать.
Вот несколько примеров неудачных пунктов, попавших в спецификацию:
- Все страницы должны отображаться за 0.1 секунды. Слово «все» следует избегать в спецификации в принципе. Да, главная страница должна открываться настолько быстро, насколько возможно. Но вы потратите недели или месяцы, пытаясь загнать в эти рамки сводный отчет, который запускают раз в год. Такие редкие и ресурсоемкие операции могут выполняться долго. Ничего страшного в этом нет.
- User-interface должен выглядеть в стиле OSX. Мода породила тысячи сайтов с заменой родных контролов, а вместе с ними боль тысяч верстальщиков и web-разработчиков. Пусть интерфейс выглядит в стиле OSX на маке и в браузере Сафари. В других системах оставьте родные элементы управления.
- На главной странице веб-сайта должен быть флеш-баннер. Возможно, для этого баннера вообще не нужен флеш, и все можно сделать средствами html/css/javascript, а аналогичный модуль у вас есть уже «из коробки». Избегайте чрезмерного уточнения технологических аспектов в спецификации.
Используйте user-stories
User-stories – один из лучших способов понять, что на самом деле нужно. User-stories могут оформляться немного по-разному, но обязательно должны содержать 3 пункта:
In order to — зачем?
As a — кто?
I want — что?
Проработав каждую такую историю, вы сможете понять, действительно ли нужно то, о чем вас просят, или на самом деле есть лучшее решение проблемы, про которое никто просто не подумал. Если I want уже сформулирован, а As a и In order to – нет, – это повод задуматься. Возможно, вы собираетесь разработать никому не нужный функционал.
Вот так могут выглядеть user-stories
- In order to be able to do direct marketing of products to existing customers,
As a marketing manager
I want customers to register personal details by joining a VIP program.
- In order to entice existing customers to register for the VIP program,
As a marketing manager
I want the system to offer free delivery on certain items to VIP customers.
- In order to save money,
As an existing customer
I want to receive information on available special offers.
Составляйте спецификацию совместно (specifying collaboratively)
![](https://habrastorage.org/storage2/559/356/7a0/5593567a0bd4a81c0744b4a19d7349e8.png)
Спецификация, составленная единолично, без команды – вторая большая ошибка и потенциальное пространство для возникновения непонимания и последующих правок. Вместо того чтобы полагаться только на одного специалиста, привлеките к составлению спецификации всю команду. Если вы работаете по скраму, пленинг-митинг – отличное время, чтобы сделать это.
Разработчики лучше понимают инфраструктуру и знают технологии, которые можно применить для решения проблемы. QA-специалисты укажут, в каких местах могут возникнуть ошибки. Product-owner – эксперт в предметной области. Вся эта информация полезна для составления спецификации. Совместная работа позволяет:
- Лучше разобраться с проблемой и найти оптимальное решение
- Сделать так, чтобы вся команда понимала новые требования одинаково
- Вовлечь всех участников в процесс
На этапе планирования лучше подключить всю команду. После того, как основной фронт работ понятен, более эффективна парная работа или небольшие совещания на 3-4 человека, чтобы прояснить сложные моменты.
![](https://habrastorage.org/storage2/879/31b/1d7/87931b1d714451471866861782fc85c8.png)
Приводите примеры (illustrating using examples)
Натуральные языки играют с нами злую шутку. Они оставляют простор для толкований, непонимания, а иногда требуют знания предметной области и/или специфического жаргона, чтобы понять, о чем идет речь. Небольшое недопонимание может привести к срывам сроков и большому количеству правок или даже переписыванию целых модулей. Вместо того чтобы долго и мучительно формулировать требования, опишите их с помощью примеров. Вместе с клиентом вы сможете найти ключевые примеры. Помощь разработчиков и тестировщиков просто неоценима в данном случае, потому что они смогут заранее указать на потенциальные проблемные области и технические ограничения.Вот как может выглядеть процесс совместной работы над спецификацией. Для примера возьмем интернет-магазин «Рога и Копыта».
- Варвара – представитель клиента
- Вася – разработчик
- Оля – QA
Варвара: Итак, мы заключили договор с издательством «ABC Press» о том, что мы будем бесплатно доставлять книги издательства, купленные у нас.
Вася: По всей России?
Варвара: Нет, что вы. Только по Москве.
Оля: А у нас в админке везде заполнено правильное издательство? Варя, ты можешь дать нам полный список книг, боюсь, можем напутать, в БД поле «издательство» — это строка.
Варвара: Да, конечно, там не так много книг.
Вася: А сколько книг мы можем доставить бесплатно? У нас есть какие-то ограничения: 5-10? Что произойдет, если в корзине будут книги других издательств?
Варвара: Это не важно, пока в корзине есть хотя бы одна книга, доставка бесплатно.
Вася: Хорошо, а что произойдет, если клиент закажет книгу и холодильник? Такая доставка будет стоить дорого…
Варвара: Да, об этом мы как-то не подумали. Давайте, я запишу и уточню, и мы обсудим это через неделю, информации уже достаточно, чтобы начать работу?
Вася: Конечно, начну набрасывать архитектуру.
Оля: А я проверю, что там с издательствами.
Через 3 дня.
Варвара (по телефону): Мы пообщались и решили, что бесплатная доставка будет доступна только для книг. Если в корзине есть что-то еще – только обычная доставка. И еще мы решили ограничить сверху количество книг – не более 10.
Вася: Ок.
В результате, мы получим ключевые примеры:
Customer type | Cart contents | Delivery |
---|---|---|
VIP | 1 book | Free |
VIP | 10 books | Free |
VIP | 11 books | Standard |
Regular | 10 books | Standard |
VIP | 5 washing machines | Standard |
VIP | 1 washing machine, 5 books | Standard |
Очищайте спецификацию (refining the specification)
![](https://habrastorage.org/storage2/07e/12e/3b1/07e12e3b15fe7ed669dac9f10fa140fa.jpg)
В ходе совместного обсуждения вы сможете достичь общего видения цели. Можно сравнить предыдущий этап с тем, что вы добыли алмаз. Алмаз сам по себе довольно ценен, но после обработки его ценность возрастет многократно.
В ходе обсуждения каждый член команды будет тянуть одеяло на себя.:
- Представители бизнеса склонны уделять слишком много внимания UI (потому что они не специалисты в технической области, UI – это единственное, что они могут «потрогать руками»).
- Разработчики склонны углубляться в детали реализации: какой фреймворк выбрать и какую технологию применить.
- Тестировщики будут параноидально искать ошибки и уязвимости.
Таким образом, первоначальный вариант наверняка получится противоречивым и избыточным.
Цель этого этапа – отделить зерна от плевел и предоставить нужное количество деталей. Спецификация должна стать единым документом для:
- Приемочных критериев
- Приемочных тестов
- Будущих регрессионных тестов
Для того чтобы максимально снизить шансы недопонимания, мы запишем сценарии в терминах Given When Then. Если вы еще не знакомы с этой формой записи, вспомните уроки математики в школе: Дано, Найти, Решение. Тут примерно то же самое.
Given — первоначальный контекст (предусловие)
When — событие (что является триггером сценария)
Then — результат, который мы хотим получить
Для нашего примера с книгами это будет выглядеть так:
Feature: Free delivery
In order to save money
As a VIP customer
I want the system to offer free delivery on certain items to me
Scenario: Free delivery
Given I am a VIP customer
And I am on product detail page
And There are only books in my shopping cart
And There are <= 10 books in my shopping cart
And I have added 'ABC Press' book to my shopping cart
When I press 'Go to checkout' button
And I have chosen 'Moscow' in 'Ship To' dropdown
Then I can choose free delivery
Подобная форма записи с одной стороны остается читаемой для
Мы можем немного изменить сценарий, чтобы обрадовать QA.
Scenario Outline: Free delivery
Given I am a VIP customer
And I am on product detail page
And There are only books in my shopping cart
And There are <bookQuantity> books in my shopping cart
And I have added 'ABC Press' book to my shopping cart
When I press 'Go to checkout' button
And I have chosen 'Moscow' in 'Ship To' dropdown
Then <deliveryType> is available
Examples:
|bookQuantity|deliveryType|
|5 |Free |
|10|Free |
|11|Standard |
В этом случае сценарий можно использовать как список тест-кейсов.
Для написания этого сценария я воспользовался SpecFlow – решением для .NET-платформы. Аналогичные инструменты есть для Java, Ruby, PHP.
Я не зря акцентирую внимание на инструменте. Как именно писать тесты с помощью Given When Then – слишком обширная тема, которую мы рассмотрим в следующей статье, а пока ограничимся базовой информацией. Тесты создаются в декларативном стиле, с помощью «шагов». Обратите внимание, что параметры из таблицы Examples будут переданы в аргументы методов.
namespace ProjectName.Specification
{
public class FreeDeliverySteps
{
[Given("I am a VIP customer")]
public void GivenIAmVipCustomer()
{
throw new NotImplementedException();
}
[Given("I am on product detail page")]
public void GivenIAmOnProductDetailPage()
{
throw new NotImplementedException();
}
[Given("I have added \'ABC Press\' book to my shopping cart")]
public void GivenIHaveAddedAbcPressBookToMyShoppingCart()
{
throw new NotImplementedException();
}
[Given("There are only books in my shopping cart")]
public void GivenThereAreOnlyBooksInMyShoppingCart()
{
throw new NotImplementedException();
}
[Given("There are (.*) books in my shopping cart")]
public void GivenThereAreBookQuantityBooksInMyShoppingCart(int bookQuantity)
{
throw new NotImplementedException();
}
[When("I press 'Go to checkout' button")]
public void WhenIPressGoToCheckoutButton()
{
throw new NotImplementedException();
}
[When("And I have chosen 'Moscow' in 'Ship To' dropdown")]
public void WhenIHaveChosenMoscowInShipToDropdown ()
{
throw new NotImplementedException();
}
[Then("Then (.*) is available")]
public void ThenDeliveryTypeIsAvailable(string deliveryType)
{
throw new NotImplementedException();
}
}
}
Более подробно про BDD можно почитать на сайте родоначальника этой парадигмы.
Внедряйте автоматизацию тестирования без изменения спецификации (automating validation without changing specification)
![](https://habrastorage.org/storage2/fa3/b26/138/fa3b261380a1b617b6a0dad975464f89.png)
Законченная спецификация послужит вам одновременно техническим заданием на разработку функционала и тестовыми сценариями для проверки правильности выполнения работы. Не откладывайте автоматизацию тестирования «на потом». Тесты позволят обнаружить ошибки на раннем этапе разработки, а значит вам не придется переделывать функционал снова и снова.
Тесты, покрывавшие часть кода функционала, который «попал под нож», либо станут красными, либо вообще перестанут выполняться (перейдут в статус Ignored), и процесс надо будет повторить сначала: написать тесты, переписать код, проверить, что все тесты проходят.
При такой организации работы Вы будете изменять спецификацию и тесты в одном месте. То есть сама спецификация станет исполняемой (executable).
Встраивайте выполнение тестов в процесс сборки и развивайте документацию (validating frequently, evolving a documentation system)
О пользе Continuous Integration на Хабре уже написано немало. Мы рассмотрим эту практику со своей колокольни. Итак, у нас есть исполняемая спецификация (спецификация с примерами и связанными с ней автоматизированными тестами). К сожалению, большая часть команды до сих пор не может «пощупать» результат проделанной работы. Не будут же менеджеры устанавливать IDE, обвешанную плагинами, и разворачивать всю систему на своей машине.Нужно встроить выполнение тестов в вашу систему сборки (если билд-сервера в организации нет, может быть это повод, для того чтобы он появился?)
Как только тесты будут выполняться регулярно, вы заметите, что автоматизированные тесты – это самый надежный источник информации о состоянии дел в проекте на данный момент. На графике ниже пример успешной итерации разработки. В первый день написана малая часть приемочных тестов и все они красные (что понятно, ведь никакого функционала у нас еще нет). Постепенно количество тестов возрастает, чтобы покрыть весь скоуп разработки. Параллельно количество зеленых тестов растет – функционал начали разрабатывать.
Количество зеленых тестов – единственный надежный критерий оценки того, что уже сделано и что предстоит.
К сожалению, практика показывает, что разработчики, пусть и из благих побуждений, могут рапортовать «да, да, завтра все заработает» месяцами. График не будет врать: фича готова, когда все ее тесты зеленые.
Слишком много красных тестов, а релиз скоро? Скорее всего, нужно принять меры, иначе функционал не будет готов в срок с должным уровнем качества.
![](https://habrastorage.org/storage2/dbd/563/9f8/dbd5639f8e52d9b7daae66f0f1c8be15.png)
С развитием продукта спецификация будет расти, а требования – меняться вслед за требованиями рынка. Даже такую спецификацию придется поддерживать и структурировать, чтобы вся информация была быстро доступна. Я готов заплатить эту цену за уверенность в том, что каждый следующий релиз пройдет без срывов сроков, сверхурочной работы, и продукт будет работать как часы.
К чему это я
Мир вокруг нас меняется очень быстро. И сейчас мир бизнеса и разработки повернулись друг к другу если не лицом, то хотя-бы в пол-оборота. Но пропасть коммуникации до сих пор не преодолена окончательно.Я начал внедрять практики Specification By Example значительно раньше, чем прочитал книгу. Многие из описанных рекомендаций приходят с опытом, многое можно почерпнуть из других источников. Действительно ценно то, что Gojko Adzic смог систематизировать эту информацию и предложить процесс.
Мой опыт полностью совпадает с опытом автора: проекты, на которых внедряются принципы Specification By Example, разрабатываются быстрее, а количество багов и правок уменьшается.
Я надеюсь, что статья будет полезна молодым командам, которые находятся в поиске подходящего процесса.
Ссылки
- Сайт книги: specificationbyexample.com
- Доклад Николая Алименкова, через который я познакомился с книгой (Thucydides — приемочные тесты нового поколения): video.yandex.ru/users/ya-events/view/809/?cauthor=ya-events&cid=75
- Еще одна статья на Хабре про Spec By Example