BDD на TypeScript: когда код говорит на языке бизнеса
Вы когда-нибудь переводили многостраничный документ с требованиями, чтобы понять, что заказчик имел в виду что-то совсем другое? Знакома ситуация, когда разработчик и аналитик смотрят на одну и ту же задачу, но видят два разных приложения? Если да, то вы уже созрели для BDD (Behaviour-Driven Development) — методологии, которая стирает границу между «хочу» и «сделано».
Сегодня мы поговорим о том, как внедрить BDD на TypeScript. Я покажу, как превратить скучные спецификации в живые, исполняемые сценарии, которые поймут и менеджер, и бэкенд-разработчик. Это не просто про тесты — это про общий язык команды.
Кстати, если вы только начинаете свой путь в TypeScript, рекомендую заглянуть в базу знаний PythonLib — там есть отличные материалы по основам. А мы идём дальше.
Что такое BDD и почему он вам нужен?
Behaviour-Driven Development — это эволюция TDD (Test-Driven Development). Если TDD фокусируется на том, «как» работает код (тесты пишутся до реализации), то BDD отвечает на вопрос «что» система должна делать с точки зрения пользователя.
Главная фишка BDD — Gherkin-синтаксис. Это структура «Дано — Когда — Тогда» (Given — When — Then), которая выглядит так:
Функционал: Авторизация пользователя
Как зарегистрированный пользователь
Я хочу войти в систему
Чтобы получить доступ к личному кабинету
Сценарий: Успешный вход
Дано пользователь с email "test@example.com" и паролем "123456"
Когда пользователь вводит корректные данные
Тогда система перенаправляет его в личный кабинет
Согласитесь, это понятнее, чем «unit-тест проверяет, что метод login() возвращает true»? Именно в этом сила BDD: тесты на TypeScript становятся документацией, которую может прочесть любой участник команды.
Когда BDD окупается?
- Сложная бизнес-логика — когда в коде много условий, цепочек действий и сценариев ошибок.
- Распределённые команды — аналитики в Москве, разработчики в Минске, тестировщики в Новосибе. BDD — их общий язык.
- Микросервисы — когда нужно чётко описать контракты взаимодействия между сервисами.
Инструменты для BDD на TypeScript
Для внедрения BDD в TypeScript у нас есть два главных инструмента: Cucumber.js (или его вариации) и Jest с плагинами. Давайте разберём оба подхода.
Cucumber.js + TypeScript: классика жанра
Cucumber — это золотой стандарт BDD. Он читает Gherkin-файлы (обычно с расширением .feature) и запускает код-степы, которые вы пишете на TypeScript.
Установка:
npm install --save-dev @cucumber/cucumber typescript ts-node
npm install --save-dev @types/node
Создаём файл features/login.feature:
Функционал: Вход в систему
Как пользователь
Я хочу авторизоваться
Чтобы получить доступ к функциям
Сценарий: Успешный вход с корректным email и паролем
Дано я открываю страницу входа
Когда я ввожу email "admin@test.com"
И я ввожу пароль "secret"
И я нажимаю кнопку "Войти"
Тогда я вижу приветственное сообщение "Добро пожаловать, Admin!"
Теперь пишем степы (шаги) на TypeScript. Создаём features/step_definitions/login.steps.ts:
import { Given, When, Then } from '@cucumber/cucumber';
import { expect } from 'chai';
// Простая имитация состояния приложения
const appState = {
currentPage: '',
loggedInUser: ''
};
Given('я открываю страницу входа', function () {
appState.currentPage = 'login';
appState.loggedInUser = '';
});
When('я ввожу email {string}', function (email: string) {
// Здесь могла бы быть работа с реальным UI, но для примера — просто сохраняем
console.log(`Введён email: ${email}`);
});
When('я ввожу пароль {string}', function (password: string) {
console.log(`Введён пароль: ${password}`);
});
When('я нажимаю кнопку "Войти"', async function () {
// Симуляция асинхронного запроса к серверу
appState.loggedInUser = 'Admin';
appState.currentPage = 'dashboard';
});
Then('я вижу приветственное сообщение {string}', function (message: string) {
expect(`Добро пожаловать, ${appState.loggedInUser}!`).to.equal(message);
});
Запускаем:
npx cucumber-js
Cucumber сам найдёт все .feature файлы и выполнит степы. Результат — зелёный отчёт, который показывает, какие сценарии прошли. Это и есть разработка через поведение в действии.
Jest + Jest-Cucumber: лёгкий старт
Если вы уже используете Jest для тестирования, подключать Cucumber целиком может быть избыточно. Для таких случаев есть jest-cucumber — библиотека, которая позволяет писать BDD-тесты прямо в Jest.
Установка:
npm install --save-dev jest-cucumber @types/jest
Создаём файл features/checkout.feature:
Функционал: Оформление заказа
Сценарий: Успешное оформление с купоном на скидку
Дано в корзине товары на сумму 5000 рублей
И у меня есть купон "SALE20" на 20% скидку
Когда я применяю купон
Тогда итоговая сумма должна быть 4000 рублей
Теперь тест-файл tests/checkout.test.ts:
import { defineFeature, loadFeature } from 'jest-cucumber';
const feature = loadFeature('./features/checkout.feature');
defineFeature(feature, test => {
let cartTotal: number;
let couponCode: string;
let finalTotal: number;
test('Успешное оформление с купоном на скидку', ({ given, when, then }) => {
given(/^в корзине товары на сумму (\d+) рублей$/, (total: string) => {
cartTotal = parseInt(total, 10); // 5000
});
given(/^у меня есть купон "(\w+)" на (\d+)% скидку$/, (code: string, percent: string) => {
couponCode = code;
// Сохраняем скидку для проверки
this.discount = parseInt(percent, 10) / 100; // 0.2
});
when('я применяю купон', () => {
finalTotal = cartTotal * (1 - this.discount); // 4000
});
then(/^итоговая сумма должна быть (\d