BDD на C#: как писать тесты, которые понимает бизнес

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

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                

Блоги

Рекомендаций по книгам