Проблема с Традиционными Требованиями
Традиционные документы требований страдают от фундаментальных недостатков:
- Неоднозначность: “Система должна быть user-friendly” означает разные вещи для разных людей
- Устаревание: Документация устаревает в момент изменения кода
- Коммуникационные пробелы: Бизнес-стейкхолдеры и разработчики интерпретируют требования по-разному
- Отсутствие валидации: Нет способа проверить, соответствует ли реализация намерению
Specification by Example (SbE) решает эти проблемы, используя конкретные примеры как основной артефакт для спецификации, имплементации и тестирования ПО.
Что Такое Specification by Example?
Specification by Example — это коллаборативный подход к определению требований и тестов. Вместо написания абстрактных спецификаций команды создают живую документацию, состоящую из исполняемых примеров, иллюстрирующих поведение системы.
Основные Принципы
1. Выводить скоуп из целей: Начинать с бизнес-целей, а не фич 2. Специфицировать коллаборативно: Бизнес, разработка и тестирование работают вместе 3. Иллюстрировать используя примеры: Конкретные сценарии, а не абстрактные описания 4. Уточнять спецификацию: Итерировать на примерах, пока они не станут четкими и тестируемыми 5. Автоматизировать валидацию: Превращать примеры в исполняемые тесты 6. Валидировать часто: Примеры служат как регрессионные тесты 7. Эволюционировать документацию: Обновлять примеры по мере эволюции системы
Процесс SbE
1. Воркшопы Примеров (Три Амиго)
Представители трех перспектив коллаборируют:
Бизнес (Product Owner): Какую ценность мы доставляем? Разработка (Инженер): Как мы это построим? Тестирование (QA): Что может пойти не так?
Повестка Воркшопа:
- Представить user story/фичу
- Мозговой штурм примерных сценариев
- Идентифицировать счастливые пути, граничные случаи, условия ошибок
- Уточнить примеры, пока все не согласятся
- Документировать в структурированном формате
2. Формат Примера
Примеры обычно следуют формату Given-When-Then (синтаксис Gherkin):
Сценарий: Премиум клиент получает скидку лояльности
Дан клиент с премиум членством
И они были членом 3 года
Когда они покупают товар стоимостью $100
Тогда они получают скидку 15%
И итог $85
3. Автоматизация
Примеры автоматизируются используя инструменты вроде:
- Cucumber: BDD фреймворк для Gherkin спецификаций
- FitNesse: Вики-основанный фреймворк acceptance тестирования
- Concordion: Превращает спецификации на простом языке в исполняемые тесты
- SpecFlow: .NET имплементация Cucumber
4. Валидация
Автоматизированные примеры выполняются непрерывно:
- Во время разработки (быстрая обратная связь)
- В CI/CD пайплайне (регрессионная безопасность)
- Как живая документация (всегда актуальна)
Конкретный Пример: E-Commerce Скидки
Традиционный Документ Требований
Система будет применять скидки на основе tier лояльности клиента.
Премиум клиенты получают более высокие скидки, чем стандартные клиенты.
Скидки варьируются в зависимости от длительности членства и суммы покупки.
Проблемы:
- Какие именно процентные скидки?
- Что определяет “премиум” клиента?
- Как длительность членства влияет на скидки?
- Что происходит в граничных условиях?
Версия Specification by Example
Feature: Скидки на Основе Лояльности
Как владелец бизнеса
Я хочу вознаграждать лояльных клиентов скидками
Чтобы они продолжали делать покупки у нас
Background:
Даны следующие tier скидок:
| Тип Членства | Годы | Скидка |
| Стандарт | 0-1 | 0% |
| Стандарт | 2-4 | 5% |
| Стандарт | 5+ | 10% |
| Премиум | 0-1 | 10% |
| Премиум | 2-4 | 15% |
| Премиум | 5+ | 20% |
Сценарий: Новый стандартный клиент не получает скидку
Дан стандартный клиент, который присоединился 6 месяцев назад
Когда он покупает товары на общую сумму $100
Тогда примененная скидка $0
И итоговая цена $100
Сценарий: Долгосрочный премиум клиент получает максимальную скидку
Дан премиум клиент, который присоединился 6 лет назад
Когда он покупает товары на общую сумму $100
Тогда примененная скидка $20
И итоговая цена $80
Сценарий: Клиент обновился до премиум в середине tenure
Дан клиент, который был стандартным 3 года
И затем обновился до премиум 2 года назад
Когда он покупает товары на общую сумму $100
Тогда его общий tenure 5 лет
И он получает премиум скидку 5+ лет в 20%
И итоговая цена $80
Сценарий: Скидка не превышает сумму покупки
Дан премиум клиент с 6 годами членства
Когда он покупает товары на общую сумму $10
Тогда примененная скидка $2
И итоговая цена $8
Но скидка никогда не превышает общую сумму покупки
Преимущества:
- Недвусмысленно: Точные проценты и условия специфицированы
- Комплексно: Граничные случаи (изменения членства, граничные значения) покрыты
- Тестируемо: Может быть автоматизировано немедленно
- Валидировано: Примеры проверены всеми тремя амиго
FitNesse: Вики-Основанные Спецификации
FitNesse превращает вики-страницы в исполняемые тесты, делая спецификации доступными для нетехнических стейкхолдеров.
Пример FitNesse: Корзина Покупок
Вики Страница (Читаемая Бизнес Стейкхолдерами):
!3 Расчет Корзины Покупок
Даны эти товары в корзине:
|script |
|start|shopping cart |
|add item|Widget |price|10.00 |
|add item|Gadget |price|25.00 |
|add item|Doohickey |price|15.00 |
|check |subtotal |50.00 |
|apply tax|8% |
|check |tax amount | 4.00 |
|check |total |54.00 |
Что если мы применим купон?
|script |
|start |shopping cart |
|add item|Widget |price|10.00 |
|add item|Gadget |price|25.00 |
|apply coupon|SAVE10 |
|check |subtotal |50.00 |
|check |discount | 5.00 |
|check |subtotal after discount|45.00|
|apply tax|8% |
|check |total |48.60 |
Java Имплементация (Fixture):
public class ShoppingCart {
private BigDecimal subtotal = BigDecimal.ZERO;
private BigDecimal taxRate = BigDecimal.ZERO;
private BigDecimal discount = BigDecimal.ZERO;
public void start() {
subtotal = BigDecimal.ZERO;
discount = BigDecimal.ZERO;
}
public void addItem(String name, BigDecimal price) {
subtotal = subtotal.add(price);
}
public BigDecimal subtotal() {
return subtotal;
}
public void applyTaxPercent(BigDecimal percent) {
taxRate = percent.divide(new BigDecimal("100"));
}
public BigDecimal taxAmount() {
return subtotalAfterDiscount().multiply(taxRate)
.setScale(2, RoundingMode.HALF_UP);
}
public void applyCoupon(String code) {
if ("SAVE10".equals(code)) {
discount = subtotal.multiply(new BigDecimal("0.10"));
}
}
public BigDecimal discountAmount() {
return discount;
}
public BigDecimal subtotalAfterDiscount() {
return subtotal.subtract(discount);
}
public BigDecimal total() {
return subtotalAfterDiscount().add(taxAmount());
}
}
Выполнение:
- Бизнес стейкхолдеры пишут примеры в вики
- Разработчики имплементируют фикстуры
- FitNesse запускает примеры против кода
- Зеленый = спецификация соответствует имплементации
- Красный = несоответствие между спецификацией и кодом
Преимущества Живой Документации
Традиционная документация устаревает. Живая документация эволюционирует с кодовой базой:
Традиционные Доки:
Последнее обновление: Март 2024
Статус: Неизвестно, все ли еще точно
Тесты: Отдельно от документации
Живая Документация:
Последний запуск: 2 минуты назад (CI билд #4523)
Статус: Все 127 примеров проходят
Тесты: Документация ЯВЛЯЮТСЯ тестами
Документация как Страховочная Сеть
Когда разработчики изменяют код:
Без Живых Доков:
- Изменить код
- Надеяться, что тесты поймают проблемы
- Вручную проверить доки
- Может быть обновить доки (вероятно нет)
- Деплоить и молиться
С Живыми Доками:
- Изменить код
- Примеры автоматически падают, если поведение изменилось
- Обновить примеры для соответствия новому поведению
- Документация автоматически актуальна
- Деплоить уверенно
Specification by Example vs. BDD
SbE и Behavior-Driven Development (BDD) тесно связаны:
BDD — это методология разработки ПО, которая подчеркивает коллаборацию и использует примеры. Это “как” построения ПО.
SbE — это техника для захвата требований используя примеры. Это “что” мы строим.
Отношение:
- BDD использует SbE как свою основную практику
- Вы можете практиковать SbE без полного принятия BDD
- BDD добавляет дополнительные практики (вездесущий язык, разработка снаружи внутрь)
Пример:
Feature: Сброс Пароля # SbE: Что мы строим
Сценарий: Пользователь запрашивает сброс пароля # SbE: Пример спецификации
Дан пользователь с email "alice@example.com"
Когда он запрашивает сброс пароля
Тогда он получает email сброса
И email содержит валидную ссылку сброса
Синтаксис Gherkin служит как SbE документация, так и BDD тестовая автоматизация.
Реальная Имплементация: Фича Банковского Перевода
Традиционный Подход
Док Требований:
Система позволит пользователям переводить деньги между счетами.
Переводы должны валидировать достаточность средств.
Применяются дневные лимиты переводов.
Отдельные Тест-Кейсы:
TC-001: Тест успешного перевода
TC-002: Тест недостаточности средств
TC-003: Тест превышения дневного лимита
Результат: Разрыв между требованиями, тестами и имплементацией.
Подход Specification by Example
Коллаборативный Воркшоп Примеров:
Product Owner: “Мы хотим разрешить переводы средств до $10,000 в день.” Разработчик: “Что происходит, если кто-то пытается перевести $9,000 дважды в один день?” Тестировщик: “Что насчет переводов между разными валютами?” PO: “Второй перевод должен провалиться. Пока только USD.” Разработчик: “Считаются ли ожидающие переводы к лимиту?” Тестировщик: “Что если кто-то переводит точно $10,000.00 против $10,000.01?”
Результирующая Спецификация:
Feature: Переводы Счета с Дневными Лимитами
Background:
Дан дневной лимит перевода $10,000 USD
И лимиты сбрасываются в полночь Тихоокеанского времени
Сценарий: Успешный перевод в пределах лимита
Дана у Алисы $5,000 на её текущем счете
И она не совершала переводов сегодня
Когда она переводит $1,000 на счет Боба
Тогда перевод успешен
И баланс Алисы $4,000
И её оставшийся дневной лимит $9,000
Сценарий: Перевод отклонен из-за недостатка средств
Дана у Алисы $500 на её текущем счете
Когда она пытается перевести $1,000 Бобу
Тогда перевод проваливается
И ошибка "Недостаточно средств"
И баланс Алисы остается $500
Сценарий: Несколько переводов приближаются к дневному лимиту
Дана у Алисы $20,000 на её счете
И она не совершала переводов сегодня
Когда она переводит $6,000 Бобу
И переводит $3,000 Кэрол
Тогда оба перевода успешны
И её оставшийся дневной лимит $1,000
Сценарий: Перевод отклонен при превышении дневного лимита
Дана у Алисы $20,000 на её счете
И она уже перевела $9,500 сегодня
Когда она пытается перевести $1,000 Бобу
Тогда перевод проваливается
И ошибка "Превышен дневной лимит перевода"
И её оставшийся дневной лимит $500
Сценарий: Граничный тест на точном лимите
Дана у Алисы $15,000 на её счете
И она уже перевела $9,999.99 сегодня
Когда она переводит $0.01 Бобу
Тогда перевод успешен
И её оставшийся дневной лимит $0.00
Сценарий: Ожидающие переводы считаются к лимиту
Дана у Алисы $15,000 на её счете
И у неё есть ожидающий перевод $8,000
Когда она пытается перевести $3,000 Бобу
Тогда перевод проваливается
И ошибка "Превышен дневной лимит перевода (включая ожидающие)"
Результаты:
- Ясность: Все знают точно, как работают лимиты
- Граничные случаи: Граничные значения (напр., $9,999.99 + $0.01) идентифицированы
- Валидация: Бизнес-логика кодифицирована в исполняемых тестах
- Документация: Всегда отражает текущее поведение системы
Частые Ошибки и Как Их Избежать
1. Написание Деталей Имплементации в Примерах
Плохо:
Дана таблица базы данных "users" содержит строку с id=123
И колонка "premium_member" true
И "membership_date" "2019-01-01"
Хорошо:
Дан премиум клиент, который присоединился 1 января 2019
Решение: Использовать бизнес-язык, скрывать детали имплементации.
2. Слишком Много Примеров
Проблема: 50 сценариев для одной фичи, в основном избыточные.
Решение:
- Фокусироваться на ключевых примерах, иллюстрирующих разные поведения
- Использовать таблицы данных для комбинаторного тестирования
- Извлекать общие паттерны в секцию Background
3. Примеры Без Assertions
Плохо:
Когда пользователь кликает кнопку submit
Тогда форма отправлена
Хорошо:
Когда пользователь кликает кнопку submit
Тогда он видит сообщение подтверждения "Спасибо за ваш submit"
И получает email подтверждения
И submit появляется в админ панели
Решение: Включать наблюдаемые результаты, а не только действия.
4. Только Технические Стейкхолдеры
Проблема: Разработчики пишут все примеры в изоляции.
Решение: Всегда включать трех амиго (бизнес, dev, QA) в воркшопы.
5. Трактовка Примеров Только Как Тестов
Проблема: Примеры написаны после завершения кода, только для тестирования.
Решение: Примеры управляют разработкой—писать их ПЕРЕД кодированием (как TDD/BDD).
Сравнение Инструментов
Инструмент | Язык | Формат | Кривая Обучения | Лучше Для |
---|---|---|---|---|
Cucumber | Многие | Gherkin | Средняя | Команды знакомые с BDD |
FitNesse | Java, .NET | Вики таблицы | Низкая | Нетехнические стейкхолдеры |
Concordion | Java, .NET | HTML/Markdown | Средняя | Команды фокусированные на документации |
SpecFlow | .NET/C# | Gherkin | Средняя | .NET магазины делающие BDD |
Gauge | Многие | Markdown | Низкая | Команды желающие читаемые спецификации |
Robot Framework | Python | Keyword-driven | Средняя | Команды тестовой автоматизации |
Начало Работы с Specification by Example
Неделя 1: Пилотная Фича
- Выбрать одну маленькую фичу
- Провести воркшоп трех амиго
- Написать 3-5 ключевых примеров
- Автоматизировать с выбранным инструментом
- Рефлексировать о том, что сработало
Неделя 2-4: Расширение
- Добавить SbE к новым фичам
- Уточнить процесс воркшопа
- Построить библиотеку фикстур
- Обучить членов команды
Месяц 2-3: Зрелая Практика
- Ретроспектива существующих фич с примерами
- Интегрировать в CI/CD
- Измерить покрытие документации
- Праздновать успех живых доков
Заключение: Примеры как Источник Истины
Specification by Example трансформирует требования из статичных документов в живые, валидированные артефакты. Делая примеры:
- Конкретными: Без неоднозначности относительно ожидаемого поведения
- Коллаборативными: Бизнес, dev и QA выравниваются рано
- Исполняемыми: Примеры верифицируют имплементацию
- Живыми: Документация эволюционирует с кодом
Результат — ПО, которое доказуемо делает то, что ожидают стейкхолдеры, документированное в формате, понятном всем, валидируемое непрерывно и всегда актуальное.
Традиционные требования спрашивают “Мы построили это правильно?” Specification by Example гарантирует, что мы строим правильную вещь, правильным способом, с доказательством, что это работает.
Начните с малого, коллаборируйте активно и позволяйте примерам управлять вашей разработкой. Ясность и уверенность, которые они приносят, трансформируют то, как ваша команда строит ПО.