C# NUnit 的 BDD
我一直在使用自制的 BDD Spec 扩展在 NUnit 中编写 BDD 样式测试,我想看看每个人的想法。它能增加价值吗?很烂吗?如果是这样为什么?那里有更好的东西吗?
这是来源: https://github.com/mjezzi/NSpec
我创建这个的原因有两个
- :为了使我的测试易于阅读。
- 生成简单的英语输出 查看规格。
下面是测试的示例:
- 因为僵尸现在似乎很流行..
给定 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<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).
I've been using a home brewed BDD Spec extension for writing BDD style tests in NUnit, and I wanted to see what everyone thought. Does it add value? Does is suck? If so why? Is there something better out there?
Here's the source:
https://github.com/mjezzi/NSpec
There are two reasons I created this
- To make my tests easy to read.
- To produce a plain english output to
review specs.
Here's an example of how a test will look:
-since zombies seem to be popular these days..
Given a Zombie, Peson, and 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;
}
}
}
And the NSpec styled tests:
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 ) );
}
}
You'll get the Spec output in the output window:
[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).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我将介绍 BDD 的一些用途,而不仅仅是框架,因为我认为对单元级 BDD 的深入理解可能会影响您创建的一些东西。总的来说,我喜欢它。这里是:
我不会将它们称为
PersonAttacksZombieTests
,而是将它们称为PersonTests
甚至PersonBehaviour
。通过这种方式,可以更轻松地找到与特定类关联的示例,并让您将它们用作文档。看起来
IsStillAlive
并不是您想要对一个人设置的那种东西;而是一种内在属性。小心地让这样的事情公开。您正在添加不需要的行为。调用 new Person(null) 似乎不是特别直观。如果我想创建一个没有武器的人,我通常会寻找一个构造函数
new Person()
。 BDD 的一个好技巧是编写您想要的 API,然后让下面的代码完成艰苦的工作 - 让代码易于使用,而不是易于编写。对我来说,这种行为和责任似乎也有点奇怪。为什么决定人生死的是人而不是僵尸?我更愿意看到这样的行为:
person.Equip(IWeapon Weapon)
)。person.Kill
)。在我看来,这似乎将行为和责任放在了更好的位置。使用不同类型的武器进行无用的攻击,而不是检查 null,还可以让您避免使用
if
语句。你需要不同的测试:除此之外,它看起来很棒。我喜欢您使用模拟的方式、字符串的流程以及测试方法本身的措辞。我也很喜欢ProveBy;它完全按照其声明进行操作,并且很好地结合了提供行为示例和将它们作为测试运行之间的区别。
I'm going to call out some uses of BDD rather than just the framework, as I think having a really great understanding of unit-level BDD might affect some of the things you create. Overall, I like it. Here goes:
Rather than calling them
PersonAttacksZombieTests
, I'd just call themPersonTests
or evenPersonBehaviour
. It makes it much easier to find the examples associated with a particular class this way, letting you use them as documentation.It doesn't look like
IsStillAlive
is the kind of thing you'd want to set on a person; rather an intrinsic property. Careful making things like this public. You're adding behaviour that you don't need.Calling
new Person(null)
doesn't seem particularly intuitive. If I wanted to create a person without a weapon, I would normally look for a constructornew Person()
. A good trick with BDD is to write the API you want, then make the code underneath do the hard work - make code easy to use, rather than easy to write.The behaviour and responsibilities also seem a bit odd to me. Why is the person, and not the zombie, responsible for determining whether the person lives or dies? I'd prefer to see behaviour like this:
person.Equip(IWeapon weapon)
).person.Kill
).That seems to me as if it's got the behaviour and responsibilities in a better place. Using a different kind of weapon for useless attacks, rather than checking for null, also allows you to avoid that
if
statement. You'd need different tests:Other than that, it looks great. I like the way you've used the mocks, the flow of strings, and the phrasing of the test methods themselves. I also quite like
ProveBy
; it's doing exactly what it says on the tin, and nicely ties up the difference between providing examples of behaviour and running them as tests.最近我已经多次提出此类问题。有很多合理的选项,您可以轻松创建自己的选项,如本文的一些答案所示。我一直致力于开发 BDD 测试框架,目的是使其能够轻松扩展到任何单元测试框架。我目前支持 MSTest 和 NUnit。它被称为 Given,并且是开源的。基本思想非常简单,Given 提供了通用功能集的包装器,然后可以为每个测试运行器实现这些功能。
以下是 NUnit 给定测试的示例:
我尽力忠实于 Dan North 的介绍中的概念BDD 博客,因此,一切都是使用给定的、when、then 规范风格完成的。它的实现方式允许您有多个给定,甚至多个时间,并且它们应该按顺序执行(仍在检查这一点)。
此外,Given 中直接包含一整套 Should 扩展。这使得像上面看到的
ShouldEqual()
调用这样的事情成为可能,但是充满了用于集合比较和类型比较等的好方法。对于那些熟悉 MSpec 的人,我基本上将它们撕掉并做了一些进行修改以使它们在 MSpec 之外工作。不过,我认为回报在于报告。测试运行器充满了您创建的场景,因此您可以一目了然地了解每个测试实际执行的详细信息,而无需深入研究代码:
此外,根据每个程序集的测试结果,使用 t4 模板创建 HTML 报告。具有匹配故事的类都嵌套在一起,并且打印每个场景名称以供快速参考。对于上述测试,报告将如下所示:
失败的测试将显示为红色,可以单击以查看异常详细信息。
差不多就这样了。我在我正在从事的几个项目中使用它,所以它仍在积极开发中,但我认为核心非常稳定。我正在寻找一种通过组合而不是继承来共享上下文的方法,因此这可能是接下来的变化之一。提出批评吧。 :)
I've been giving this sort of question a lot of though recently. There are a lot of reasonable options out there, and you can create your own easily, as displayed in some of the answers in this post. I've been working on a BDD testing framework with the intent being to make it easily extended to any unit testing framework. I currently support MSTest and NUnit. Its called Given, and it's opensource. The basic idea is pretty simple, Given provides wrappers for common sets of functionality which can then be implemented for each test runner.
The following is an example of an NUnit Given test:
I tried my best to stay true to the concepts from Dan North's Introducting BDD blog, and as such, everything is done using the given, when, then style of specification. The way it is implemented allows you to have multiple givens and even multiple when's, and they should be executed in order (still checking into this).
Additionally, there is a full suite of Should extensions included directly in Given. This enables things like the
ShouldEqual()
call seen above, but is full of nice methods for collection comparison and type comparison, etc. For those of you familiar with MSpec, i basically ripped them out and made some modifications to make them work outside of MSpec.The payoff, though, I think, is in the reporting. The test runner is filled with the scenario you've created, so that at a glance you can get details about what each test is actually doing without diving into the code:
Additionally, an HTML report is created using t4 templating based on the results of the tests for each assembly. Classes with matching stories are all nested together, and each scenario name is printed for quick reference. For the above tests the report would look like this:
Failed tests would be colored red and can be clicked to view the exception details.
That's pretty much it. I'm using it in several projects I'm working on, so it is still being actively developed, but I'd describe the core as pretty stable. I'm looking at a way to share contexts by composition instead of inheritance, so that will likely be one of the next changes coming down the pike. Bring on the criticism. :)
我的问题是
"something".ProveBy()
与稍后显示的文本不匹配(“当......它应该......”)。我认为BDD的理念是让测试措辞和测试报告尽可能保持相似。My issue with this is having
"something".ProveBy()
does not match the text being displayed later ("When ... it should ..."). I think the concept of BDD is to keep the test wording and the test report as similar as possible.试试这个,
UBADDAS - 用户行为和领域驱动的接受故事
在这里找到 - http://kernowcode.github.io/ UBADDAS/
它会产生这样的控制台输出
Try this one,
UBADDAS - User Behaviour and Domain Driven Acceptance Stories
found here - http://kernowcode.github.io/UBADDAS/
It produces console output like this
您还可以看看这个小图书馆:
https://www.nuget.org/packages/Heleonix.Testing.NUnit/< /a>
您可以使用给定/何时/那么或安排/行动/断言样式来描述测试。
You might also have a look at the small library:
https://www.nuget.org/packages/Heleonix.Testing.NUnit/
You can describe tests in Given/When/Then or Arrange/Act/Assert styles.