BDD для С# NUnit

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

Здесь источник: https://github.com/mjezzi/NSpec

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

  • Чтобы мои тесты легко читались.
  • Чтобы создать простой английский вывод для обзор.

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

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

Учитывая зомби, песо и 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).

Ответ 1

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

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

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

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

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

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

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

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

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

Ответ 2

Я часто задавал такой вопрос, хотя недавно. Там есть много разумных вариантов, и вы можете легко создавать свои собственные, как показано в некоторых ответах в этом сообщении. Я работаю над базой тестирования 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);
    }
}

Я старался оставаться верным концепциям из блог Dan North Introducting BDD, и как таковое, все делается с использованием данного, когда, то стиль спецификации. Способ, которым он реализован, позволяет вам иметь несколько givens и даже multiple when's, и они должны выполняться по порядку (все еще проверяя это).

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

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

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

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

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

Ответ 3

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

Ответ 4

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

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