适用于C#NUnit的BDD

我一直在使用家庭酿造的BDD Spec扩展来在NUnit中编写BDD样式测试,我想看看每个人都在想什么。 它增加了价值吗? 真是太糟糕了? 如果是这样的话? 那里有更好的东西吗?

这是源: https : //github.com/mjezzi/NSpec

我创建这个有两个原因

  1. 使我的测试易于阅读。
  2. 产生简单的英语输出以查看规格。

以下是测试外观的示例:

– 这些天僵尸似乎很受欢迎..

鉴于Zombie,Peson和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(); 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). 

我将调用BDD的一些用途,而不仅仅是框架,因为我认为对单元级BDD有一个非常好的理解可能会影响你创建的一些东西。 总的来说,我喜欢它。 开始:

而不是称他们为PersonAttacksZombieTests ,我只是称他们为PersonTests甚至是PersonBehaviour 。 通过这种方式查找与特定类关联的示例变得更加容易,可以将它们用作文档。

它看起来不像IsStillAlive是你想要设置在某个人身上的那种东西; 而是一种固有的财产。 仔细制作像这样的公共场合。 您正在添加不需要的行为。

调用new Person(null)似乎并不特别直观。 如果我想创建一个没有武器的人,我通常会寻找一个构造函数new Person() 。 BDD的一个好方法就是编写你想要的API,然后让下面的代码做好工作 – 使代码易于使用,而不是易于编写。

我的行为和责任似乎有点奇怪。 为什么这个人而不是僵尸负责确定这个人是生还是死? 我更愿意看到这样的行为:

  • 一个人可以装备武器(通过person.Equip(IWeapon weapon) )。
  • 如果一个人没有武器就会以拳头开始。
  • 当该人攻击僵尸时,该人使用僵尸上的武器。
  • 武器决定了僵尸的生命或死亡。
  • 如果僵尸还活着,它会反击。 僵尸会杀死这个人(通过person.Kill )。

在我看来,好像它的行为和责任在一个更好的地方。 使用不同类型的武器进行无用的攻击,而不是检查null,也可以避免使用if语句。 你需要不同的测试:

  • 使用它时,拳头不应该杀死僵尸
  • 使用电锯时,电锯会杀死僵尸
  • 一个人在攻击僵尸时应该使用装备好的武器
  • 如果没有其他武器,一个人应该配备拳头
  • 僵尸应该在它还活着的时候进行攻击。
  • 僵尸如果死了就不应该反击。
  • 如果被杀,僵尸应该死亡。
  • 如果被杀,一个人应该死

除此之外,它看起来很棒。 我喜欢你使用模拟的方式,字符串的流动以及测试方法本身的措辞。 我也很喜欢ProveBy ; 它完全按照它在锡上所说的那样做,很好地将提供行为示例和运行它们作为测试之间的区别。

我最近一直在提出这类问题。 有很多合理的选择,你可以轻松创建自己的选项,如本文的一些答案中所示。 我一直在研究BDD测试框架,目的是使其易于扩展到任何unit testing框架。 我目前支持MSTest和NUnit。 它叫做Given ,它是开源的。 基本思路非常简单,给出了常用function集的包装器,然后可以为每个测试运行器实现。

以下是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的BDD博客介绍中的概念,因此,所有内容都是使用给定的,何时的,然后是规范的风格来完成的。 它的实现方式允许你有多个givens甚至多个when,它们应该按顺序执行(仍然检查这个)。

此外,还有一套完整的Should扩展包含在Given中。 这样可以实现上面看到的ShouldEqual()调用,但是有很多很好的方法可以进行集合比较和类型比较等。对于那些熟悉MSpec的人来说,我基本上将它们撕掉并进行了一些修改以使它们在MSpec。

不过,我认为,收益来自报告。 测试运行器充满了您创建的场景,因此您可以一眼就看到每个测试实际执行的操作的详细信息,而无需深入了解代码: 测试跑步者

此外,基于每个程序集的测试结果,使用t4模板创建HTML报告。 具有匹配故事的类都嵌套在一起,并打印每个方案名称以供快速参考。 对于上述测试,报告看起来像这样: 报告示例

失败的测试将显示为红色,可以单击以查看exception详细信息。

这就是它。 我在我正在研究的几个项目中使用它,所以它仍在积极开发中,但我将核心描述为非常稳定。 我正在寻找一种通过组合而不是inheritance来共享上下文的方法,因此这可能是下一次改变派克的变化之一。 引发批评。 🙂

我的问题是"something".ProveBy()与后来显示的文本不匹配(“当……它应该……”)。 我认为BDD的概念是使测试措辞和测试报告尽可能相似。

试试这个,

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 

您可能还要查看小型库: https : //www.nuget.org/packages/Heleonix.Testing.NUnit/您可以在Given / When / Then或Arrange / Act / Assert样式中描述测试。