BDD для C # NUnit - PullRequest
       11

BDD для C # NUnit

10 голосов
/ 16 января 2011

Я использовал расширение BDD Spec, созданное в домашних условиях, для написания тестов в стиле BDD в NUnit, и я хотел узнать, что все думают. Это добавляет ценность? Это сосать? Если так, то почему? Есть ли что-нибудь лучше там?

Вот источник: https://github.com/mjezzi/NSpec

Есть две причины, по которым я создал это

  1. Для удобства чтения моих тестов.
  2. Чтобы создать простой английский вывод обзор спецификации.

Вот пример того, как будет выглядеть тест:

- так как зомби, кажется, популярны в наши дни ..

С учетом зомби, Песона и IWeapon:

namespace Project.Tests.PersonVsZombie
{
    public class Zombie
    {

    }

    public interface IWeapon
    {
        void UseAgainst( Zombie zombie );
    }

    public class Person
    {
        private IWeapon _weapon;

        public bool IsStillAlive { get; set; }

        public Person( IWeapon weapon )
        {
            IsStillAlive = true;
            _weapon = weapon;
        }

        public void Attack( Zombie zombie )
        {
            if( _weapon != null )
                _weapon.UseAgainst( zombie );
            else
                IsStillAlive = false;
        }
    }
}

И тесты в стиле NSpec:

public class PersonAttacksZombieTests
{
    [Test]
    public void When_a_person_with_a_weapon_attacks_a_zombie()
    {
        var zombie = new Zombie();
        var weaponMock = new Mock<IWeapon>();
        var person = new Person( weaponMock.Object );

        person.Attack( zombie );

        "It should use the weapon against the zombie".ProveBy( spec =>
            weaponMock.Verify( x => x.UseAgainst( zombie ), spec ) );

        "It should keep the person alive".ProveBy( spec =>
            Assert.That( person.IsStillAlive, Is.True, spec ) );
    }

    [Test]
    public void When_a_person_without_a_weapon_attacks_a_zombie()
    {
        var zombie = new Zombie();
        var person = new Person( null );

        person.Attack( zombie );

        "It should cause the person to die".ProveBy( spec =>
            Assert.That( person.IsStillAlive, Is.False, spec ) );
    }
}

Вы получите выход Spec в окне вывода:

[PersonVsZombie]

- PersonAttacksZombieTests

    When a person with a weapon attacks a zombie
        It should use the weapon against the zombie
        It should keep the person alive

    When a person without a weapon attacks a zombie
        It should cause the person to die

2 passed, 0 failed, 0 skipped, took 0.39 seconds (NUnit 2.5.5).

Ответы [ 5 ]

10 голосов
/ 17 января 2011

Я собираюсь назвать некоторые варианты использования BDD, а не просто фреймворк, так как я думаю, что действительно хорошее понимание BDD на уровне единиц может повлиять на некоторые вещи, которые вы создаете.В целом мне это нравится.Вот так:

Вместо того, чтобы называть их PersonAttacksZombieTests, я бы просто назвал их PersonTests или даже PersonBehaviour.Это значительно упрощает поиск примеров, связанных с определенным классом, и позволяет использовать их в качестве документации.

Не похоже, что IsStillAlive - это то, что вы хотели бы установитьна человека;скорее внутренняя собственность.Тщательно делайте подобные вещи публичными.Вы добавляете поведение, которое вам не нужно.

Вызов new Person(null) не кажется особенно интуитивным.Если бы я хотел создать человека без оружия, я бы обычно искал конструктора new Person().Хороший трюк с BDD - это написать API, который вы хотите, а затем заставить его выполнять тяжелую работу - сделать код простым в использовании, а не простым в написании.

Поведение и обязанности также кажутся немного страннымимне.Почему человек, а не зомби, отвечает за определение того, живет или умирает человек?Я бы предпочел видеть такое поведение:

  • Человек может быть экипирован оружием (через person.Equip(IWeapon weapon)).
  • Человек начинает с кулака, если у него неторужие.
  • Когда человек нападает на зомби, он использует оружие для зомби.
  • Оружие определяет, живет ли зомби или умрет.
  • Если зомбивсе еще жив, это нападает назад.Зомби убьет человека (через person.Kill).

Мне кажется, что у него есть поведение и обязанности в лучшем месте.Использование другого вида оружия для бесполезных атак, а не проверка на ноль, также позволяет вам избежать этого утверждения if.Вам понадобятся разные тесты:

  • Кулак не должен убивать зомби при использовании против него
  • Бензопила должна убивать зомби при использовании против него
  • AЧеловек должен использовать свое вооружение при атаке зомби
  • Человек должен быть вооружен кулаком, если у него нет другого оружия
  • Зомби должен атаковать в ответ, когда он еще жив.
  • Зомби не должен атаковать в ответ, если он мертв.
  • Зомби должен умереть, если его убьют.
  • Человек должен умереть, если его убьют.

Кроме этого, это выглядит здорово.Мне нравится, как вы использовали макеты, последовательность строк и формулировку самих тестовых методов.Мне тоже очень нравится ProveBy;он делает именно то, что говорит на жестяной коробке, и прекрасно связывает разницу между предоставлением примеров поведения и выполнением их в виде тестов.

4 голосов
/ 10 апреля 2013

Я задавал подобные вопросы много, хотя в последнее время.Есть много разумных вариантов, и вы можете легко создать свой собственный, как показано в некоторых ответах в этом посте.Я работал над средой тестирования BDD с целью облегчить ее распространение на любую среду модульного тестирования.В настоящее время я поддерживаю MSTest и NUnit.Его называют , учитывая , и это с открытым исходным кодом.Основная идея довольно проста, поскольку Given предоставляет оболочки для общих наборов функциональных возможностей, которые затем можно реализовать для каждого участника теста.

Ниже приведен пример теста NUnit Given:

[Story(AsA = "car manufacturer",
       IWant = "a factory that makes the right cars",
       SoThat = "I can make money")]
public class when_building_a_toyota : Specification
{
    static CarFactory _factory;
    static Car _car;

    given a_car_factory = () =>
                              {
                                  _factory = new CarFactory();
                              };

    when building_a_toyota = () => _car = _factory.Make(CarType.Toyota);

    [then]
    public void it_should_create_a_car()
    {
        _car.ShouldNotBeNull();
    }

    [then]
    public void it_should_be_the_right_type_of_car()
    {
        _car.Type.ShouldEqual(CarType.Toyota);
    }
}

Я старался изо всех сил, чтобы оставаться верным понятиям из блога Дэна Норта «Вводящий BDD », и поэтому все делается с использованием заданного, когда и тогда стиля спецификации.То, как это реализовано, позволяет вам иметь несколько данных и даже несколько когда, и они должны выполняться по порядку (все еще проверяя это).

Кроме того, имеется полный набор расширений If, включенных непосредственно в Given.Это разрешает такие вещи, как вызов ShouldEqual(), показанный выше, но полон хороших методов для сравнения коллекций, сравнения типов и т. Д. Для тех из вас, кто знаком с MSpec, я в основном разорвал их и сделал некоторые модификации, чтобы они работали внеMSpec.

Окупаемость, однако, я думаю, в отчетности.Бегущий по тестам заполнен созданным вами сценарием, так что сразу вы можете получить подробную информацию о том, что фактически делает каждый тест, не углубляясь в код: Test Runner

Кроме того, HTML-отчетсоздан с использованием шаблонов t4 на основе результатов тестов для каждой сборки.Классы с соответствующими историями все вложены вместе, и имя каждого сценария напечатано для быстрого ознакомления.Для приведенных выше тестов отчет будет выглядеть следующим образом: Report Example

Неудачные тесты будут окрашены в красный цвет и их можно будет щелкнуть, чтобы просмотреть подробности исключения.

Вот и все.Я использую его в нескольких проектах, над которыми я работаю, поэтому он все еще активно разрабатывается, но я бы назвал ядро ​​довольно стабильным.Я ищу способ разделять контексты по составу, а не по наследству, так что это, вероятно, будет одним из следующих изменений, которые появятся в будущем.Ввести на критику.:)

2 голосов
/ 16 января 2011

Моя проблема, связанная с этим, заключается в том, что "something".ProveBy() не соответствует тексту, отображаемому позже («Когда ... это должно ...»). Я думаю, что концепция BDD заключается в том, чтобы формулировка теста и протокол теста были максимально похожими.

0 голосов
/ 04 марта 2018

Вы также можете взглянуть на небольшую библиотеку: https://www.nuget.org/packages/Heleonix.Testing.NUnit/ Вы можете описать тесты в стилях Given / When / Then или Arrange / Act / Assert.

0 голосов
/ 13 июня 2014

Попробуйте это,

UBADDAS - Поведение пользователей и истории, основанные на домене

найдено здесь - http://kernowcode.github.io/UBADDAS/

Выводит консоль вот так

I want to register a new user 
  So that Increase customer base
       As user
    Given Register customer
     When Confirm customer registration
     Then Login customer
...