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