BDD на C#: когда тесты говорят на языке бизнеса
Представьте: вы — разработчик C#. К вам приходит product owner и говорит: «Сделайте так, чтобы после оплаты заказа пользователь получал письмо, но только если сумма больше 1000 рублей». Вы киваете, уходите в код, пишете логику, покрываете её юнит-тестами. Через неделю вы показываете результат. PO смотрит, хмурится и говорит: «Я имел в виду другое».
Знакомо? Это классическая проблема разрыва между бизнесом и разработкой. И именно её решает BDD на C# — Behaviour-Driven Development, или разработка через поведение.
В этой статье мы разберём, что такое BDD, как он отличается от TDD, и главное — как внедрить BDD в C#-проект с помощью SpecFlow и xUnit. Вы увидите реальные примеры кода, научитесь писать сценарии на Gherkin и превратите их в автоматические тесты. Поехали!
Что такое BDD и зачем он нужен?
BDD (Behaviour-Driven Development) — это методология разработки, при которой требования к системе описываются через поведение пользователя. Вместо сухих технических спецификаций вы пишете сценарии на естественном языке, которые понимает и бизнес, и разработчик.
Ключевая идея BDD: «Не проверяйте, что код работает. Проверяйте, что система ведёт себя так, как ожидает бизнес».
В отличие от TDD (Test-Driven Development), где вы сначала пишете юнит-тест, а потом код, BDD фокусируется на сценариях использования. BDD-тесты — это не проверка метода или класса, а проверка целого пользовательского сценария.
Три кита BDD
- Единый язык — все участники проекта (PO, аналитик, тестировщик, разработчик) говорят на одном языке. Обычно это Gherkin.
- Сценарии — каждый функциональный элемент описывается через Given-When-Then (Дано-Когда-Тогда).
- Автоматизация — сценарии превращаются в автоматические тесты, которые запускаются как часть CI/CD.
Теперь давайте посмотрим, как это выглядит в коде на C#.
Инструменты для BDD на C#
Для реализации BDD в экосистеме .NET есть два основных инструмента:
- SpecFlow — самый популярный BDD-фреймворк для C#. Он парсит Gherkin-сценарии и связывает их с кодом.
- xUnit / NUnit / MSTest — тестовые раннеры, которые выполняют BDD-тесты.
Мы будем использовать SpecFlow + xUnit. Установка простая — через NuGet:
dotnet add package SpecFlow
dotnet add package SpecFlow.xUnit
dotnet add package xunit
dotnet add package xunit.runner.visualstudio
После установки создайте в проекте папку Features — именно там будут жить ваши .feature файлы со сценариями.
Пишем первый BDD-сценарий на Gherkin
Gherkin — это язык описания поведения. Он использует ключевые слова: Feature, Scenario, Given, When, Then, And, But.
Давайте опишем простой сценарий: пользователь пытается оплатить заказ, и если сумма больше 1000 рублей, ему приходит письмо.
Создайте файл Payment.feature в папке Features:
Feature: Payment Notification
As a customer
I want to receive an email after payment
So that I have a confirmation of my purchase
Scenario: Successful payment over 1000 rubles sends notification
Given a customer with balance 5000 rubles
When the customer pays an order of 1500 rubles
Then the customer should receive an email notification
Scenario: Payment under 1000 rubles does not send notification
Given a customer with balance 5000 rubles
When the customer pays an order of 500 rubles
Then the customer should not receive any email notification
Обратите внимание: здесь нет ни строчки кода на C#. Это plain text, который может написать даже аналитик. Но именно этот текст станет вашим тестом.
Связываем Gherkin с C#: Step Definitions
Теперь нужно «оживить» сценарии. Для этого создаём Step Definitions — классы на C#, которые реализуют каждый шаг из Gherkin.
Создайте файл PaymentSteps.cs:
using TechTalk.SpecFlow;
using Xunit;
[Binding]
public class PaymentSteps
{
private decimal _balance;
private decimal _orderAmount;
private bool _emailSent;
[Given(@"a customer with balance (.*) rubles")]
public void GivenCustomerWithBalance(decimal balance)
{
_balance = balance;
}
[When(@"the customer pays an order of (.*) rubles")]
public void WhenCustomerPaysOrder(decimal amount)
{
_orderAmount = amount;
// Имитация логики оплаты
if (_balance >= _orderAmount)
{
_balance -= _orderAmount;
// Отправка письма только если сумма > 1000
if (_orderAmount > 1000)
{
_emailSent = true;
}
}
}
[Then(@"the customer should receive an email notification")]
public void ThenCustomerReceivesEmail()
{
Assert.True(_emailSent);
}
[Then(@"the customer should not receive any email notification")]
public void ThenCustomerDoesNotReceiveEmail()
{
Assert.False(_emailSent);
}
}
Что здесь происходит? Атрибут [Binding] говорит SpecFlow, что этот класс содержит шаги. Атрибуты [Given], [When], [Then] связывают регулярные выражения из Gherkin с методами C#. Обратите внимание на (.*) — это захват параметров из сценария.
Запускаем BDD-тесты
Теперь откройте терминал и выполните:
dotnet test
Если всё сделано правильно, вы увидите зелёные галочки — оба сценария пройдены. Но самое интересное: если бизнес-логика изменится (например, порог отправки письма станет 2000 рублей), вы просто меняете сценарий в .feature файле, и тест падает. Никаких «ой, я забыл обновить тесты».
Продвинутые возможности BDD на C#
Scenario Outline — сценарии с параметрами
Если у вас много похожих сценариев, используйте Scenario Outline с таблицей примеров:
Scenario Outline: Payment notification threshold
Given a customer with balance <balance> rubles
When the customer pays an order of <amount> rubles
Then the notification status should be <notification>
Examples:
| balance | amount | notification |
| 5000 | 1500 | true |
| 5000 | 500 | false |
| 100 | 2000 | false |
В Step Definitions нужно будет добавить соответствующий Then:
[Then(@"the notification status should be (.*)")]
public