什么是最成熟的.NET的BDD框架?

我们一直在使用BDD行为驱动开发 (来自Dan North的观点)作为logging用户验收testing的机制,并在一些项目上推动开发,取得了不错的成功。 迄今为止,我们还没有实际自动化testing。

我现在正在寻找自动化的testing,但我不知道哪个行为框架回来。 到目前为止,NBe似乎已经成为了先行者,但是我还有其他的看法吗? 目前是否有明确的“赢家”?

快速回答

一个非常重要的观点是有两种行为驱动开发的风格。 两种口味是xBehavexSpec

xBehave BDD:SpecFlow

SpecFlow(非常类似于Ruby堆栈中的黄瓜 )在促进xBehave BDDtesting作为验收标准方面非常出色。 但是,它并没有提供一种在单元级别编写行为testing的好方法。 还有其他一些xBehavetesting框架,但是SpecFlow已经得到了很多的牵引。

xSpec BDD:MSpec

客观地说。 鉴于可用的上下文规范框架,MSpec已经是最长的,是.Net社区中使用最广泛的上下文/规范框架。

其他的xSpec BDD框架:NSpec

我个人会推荐NSpec (直接受RSpec的Ruby启发)。 充分披露,我是NSpec的作者之一。 你可以通过简单的使用NUnit或者MSTest来完成BDD …但是它们有点不足(增加构build上下文真的很困难)。 MSpec也是一个选项,是.Net最成熟的上下文/规范框架。 但是 ,在NSpec中只有一些更简单的事情。

长的答案

BDD的两种风格主要是由于它们提供的正交好处而存在的。

xBehave的优点和缺点(GWT语法)

优点

  • 通过一种被称为(例如,给定….,给定…,当…,当…..,然后….)接着)
  • 然后通用的方言可以映射到可执行的代码,certificate你实际上完成了你所说的完成的事情
  • 方言是狭隘的,所以企业必须消除歧义的要求,并使其适合句子。

缺点

  • 尽pipexBehave方法对于推动高级验收标准是有利的,但是通过属性将英语映射到可执行代码所需的周期使得在单元级别上驱逐一个域是不可行的。
  • 将普通方言映射到testing是PAINFUL(在你的正则expression式上加速)。 业务创build的每个句子都必须通过属性映射到可执行的方法。
  • 通用的方言必须严格控制,以便pipe理制图不会失控。 任何时候你改变一个句子,你必须find直接关系到这个句子的方法,并修正正则expression式的匹配。

xSpec的优点和缺点(上下文/规格)

优点

  • 允许开发人员逐步build立上下文。 可以为testing设置上下文,并且可以针对该上下文执行一些断言。 然后可以指定更多的上下文(build立在已经存在的上下文上),然后指定更多的testing。
  • 没有限制的语言。 开发人员可以对系统的某个部分如何performance更有performance力。
  • 英语和普通方言之间不需要映射(因为没有一个)。

缺点

  • 不像业务平易近人。 让我们面对现实,企业不喜欢消除他们想要的东西。 如果我们给他们一个基于上下文的方法来处理BDD,那么这个句子就会写成“Just make it Work”。
  • 一切都在代码中。 上下文文档是交织在代码中(这就是为什么我们不必担心将英文映射到代码)
  • 考虑到限制较less的措辞,不可读。

样品

保龄球卡塔是一个很好的例子。

SpecFlow示例

以下是SpecFlow中的规格说明(同样,这是一个很好的验收testing,因为它直接与业务进行通信):

function文件

function文件是testing的常用方言。

特点:分数计算 
  为了知道我的performance
  作为一名球员
  我希望系统计算我的总分

情景:天沟游戏
  给一个新的保龄球游戏
  当我所有的球都落在排水沟里
  那么我的总分应该是0

场景:单引脚
  给一个新的保龄球游戏
  当我刚刚打到1针
  那么我的总分应该是1

步骤定义文件

步骤定义文件是testing的实际执行,该文件包含SpecFlow的映射

[Binding] public class BowlingSteps { private Game _game; [Given(@"a new bowling game")] public void GivenANewBowlingGame() { _game = new Game(); } [When(@"all of my balls are landing in the gutter")] public void WhenAllOfMyBallsAreLandingInTheGutter() { _game.Frames = "00000000000000000000"; } [When(@"I've hit exactly 1 pin")] public void When1PinIsHit() { _game.Frames = "10000000000000000000"; } [Then(@"my total score should be (\d+)")] public void ThenMyTotalScoreShouldBe(int score) { Assert.AreEqual(score, _game.Score); } } 

MSpec示例,xSpec,上下文/规范

public class describe_BowlingKata { public static Game game; public class when_all_balls_land_in_the_gutter : describe_BowlingKata { Establish ctx = () => game = new Game(); Because of = () => game.Frames = "00000000000000000000"; It should_have_a_score_of_0 = () => game.Score.ShouldBe(0); } public class when_a_single_pin_is_hit : describe_BowlingKata { Establish ctx = () => game = new Game(); Because of = () => game.Frames = "10000000000000000000"; It should_have_a_score_of_1 = () => game.Score.ShouldBe(1); } }
public class describe_BowlingKata { public static Game game; public class when_all_balls_land_in_the_gutter : describe_BowlingKata { Establish ctx = () => game = new Game(); Because of = () => game.Frames = "00000000000000000000"; It should_have_a_score_of_0 = () => game.Score.ShouldBe(0); } public class when_a_single_pin_is_hit : describe_BowlingKata { Establish ctx = () => game = new Game(); Because of = () => game.Frames = "10000000000000000000"; It should_have_a_score_of_1 = () => game.Score.ShouldBe(1); } } 

NSpec示例,xSpec,上下文/规范

这是同样的保龄球kata NSpec的例子:

class describe_BowlingGame : nspec { Game game; void before_each() { game = new Game(); } void when_all_my_balls_land_in_the_gutter() { before = () => game.Frames = "00000000000000000000"; it["should have a score of 0"] = () => game.Score.should_be(0); } void when_a_single_pin_is_it() { before = () => game.Frames = "10000000000000000000"; it["should have a score of 1"] = () => game.Score.should_be(1); } }
class describe_BowlingGame : nspec { Game game; void before_each() { game = new Game(); } void when_all_my_balls_land_in_the_gutter() { before = () => game.Frames = "00000000000000000000"; it["should have a score of 0"] = () => game.Score.should_be(0); } void when_a_single_pin_is_it() { before = () => game.Frames = "10000000000000000000"; it["should have a score of 1"] = () => game.Score.should_be(1); } } 

随着你做越来越多的BDD,你会发现BDD的xBehave和xSpec风格都是需要的。 xBehave更适合验收testing,xSpec更适合unit testing和域驱动devise。

MSpec vs NSpec

像年龄和稳定性这样的客观指标应该是一个因素,我鼓励大家考虑这个因素。 但是请注意,新的框架可能会提供更简洁的API,更好地使用语言结构,并为过去的框架吸取经验教训 。 MSpec提供了Given,As,It和Cleanup的结构,但是它们的代价是:所有成员的静态初始化,类爆炸,由于其独特的代表使用,它在语法上是刚性的。 你会发现最简单的MSpectesting在NSpec中更简单。 MSpec和NSpec中都有一个更复杂的testing套件。

xUnit,MSpec和NSpec的比较: https ://gist.github.com/amirrajan/6701522

相关链接

RSpec vs黄瓜(RSpec故事)

BDD与黄瓜和rspec – 何时是多余的?

查看SpecFlow 。

这是一个受Cucumber启发的工具,旨在为今天的.NET项目的验收testing驱动开发和行为驱动开发提供一个务实和无摩擦的方法。

VisualStudio整合看起来特别有前途。

StoryQ看起来像是NBehave的Fluent界面。 我肯定会检查出来。

我不认为有真正的“赢家”。 其他框架包括SpecUnit.NET项目和MSpec也显示承诺与一个Gallio适配器的开始。 从技术上讲,IronRuby即将出现, rSpec 可能成为那些准备走出头来的select。 NBehave + NSpec可能是最好的自动化最年长的框架,但我发现自己反对过度冗长的语法。

我会检查他们,并select最适合您的项目风格的框架。 他们都是开源软件,所以很难失去,真正的好处就是转向BDD。

我个人会推荐BDDfy ,在我看来这很棒! 它是开源的,支持常规和stream畅的场景描述,涵盖了很好的验收和unit testing。 你可以在GitHub上find它。

Robot Framework也可以和IronPython一起在.Net中使用ATDD或BDD。 我认为Robot Framework的expression能力比例如。 SpecFlow或NSpec的。 它不会强制您使用某种语法,而是使用关键字驱动的方法。 如果您正在testingWeb应用程序,那么Selenium2Library提供了Selenium WebDriver的绑定。

还有Specter ,它在Boo中定义了一个DSL,使其更加自然。

我通常会selectNBehave,结合MbUnit以及您需要的任何DI / Mocking框架。 Jim Burger公平的评论说,NBehave非常冗长,而且我发现自己有时也会使用剪贴。 不过,它的效果很好 – 我使用的是Gallio的ReSharper插件,所以我只是得到一个额外的窗口显示。 尽pipe如此,还没有尝试与ccnet。

查看这个博客文章及其意见鼓舞人心的想法: http : //haacked.com/archive/2008/08/23/introducing-subspec.aspx/

Concordion.NET不仅支持BDD,而且还支持ATDD: http : //assertselenium.com/2012/11/05/difference-between-tdd-bdd-atdd/ 规范使用HTML 以简单的英文书写 。 恕我直言,这是Concordion.NET的好处之一。 HTML文档可以组织成一个可导航的结构来build立一个生动的文档系统 。 而且,由于testing是针对系统运行的,因此您可以确信文档始终是最新的。

我正在BDD的第一场比赛中开始我的团队的一个小应用程序。 我们select做这项工作的工具是:使用Selenium Webdriver进行xBehave故事的Specflow和使用Machine.Fakes.Moq进行xSpecunit testing的automocking容器的MSpec。 由于Specflow和MSpec的支持,Resharper成为我们的故事跑步者和规格跑者。 与R#本地整合到视觉工作室是我们的一个关键特性。

由于我们的devise都是MVC3,我们也将把MVC控制器应用到MVC3中。 这将允许我们直接针对协调者编写规范。 然后让我们直接写我们的应用程序的使用情况。

还请查阅UBADDAS,具体到UAT中find

https://github.com/KernowCode/UBADDAS

这里有一个解释

http://kernowcode.wordpress.com/(2014年; 6月)

你可以写这样的testing

 [Test] public void IWantToRegisterANewUser() { var user = new User(); ICustomer customer = new Customer(); SoThat(MyBusinessValue.IncreaseCustomerBase) .As(user) .Given(customer.Register) .When(customer.Confirm_Registration) .Then(customer.Login); } 

并产生这一点

 I want to register a new user so that Increase customer base as user given Register customer when Confirm customer registration then Login customer 

由于我现在正在处理BDD系统testing安全关键应用程序,所以我只能分享我的经验,您不应该低估“用自然语言编写的testing”而不是“代码”的威力。

我们一直专注于提供一种function语言,任何人都可以在没有任何技术背景和编程经验的情况下理解(参见上面的specflow例子),我们做得很好。 除了我从来没有向任何人解释语法的事实之外,每个人都立即明白了testing,开发人员,pipe理人员甚至testing人员的意思。

我会以任何方式避免用上面的MSpec编程语言编写的testing。 如果我在经理的办公室出现这样的testing,他会把我踢出去的。 但他有兴趣阅读基于小黄瓜语法的testing。 能够阅读和修改testing的人越多越好。

最后但并非最不重要的是,这些testing可移植到任何其他编程语言,任何其他平台,任何其他testing自动化工具,无需任何更改。

再一次,答案是再一次:

不要在乎工具及其function本身,select一个工具,可以随时轻松切换到另一个工具,而不会丢失信息。 工具来来往往,我的testing应该持续更久。 🙂

我可以推荐使用SpecFlow。 您可以完全访问完整的.Net库及其所有function,您的testing可以保持便携。 如果你不介意可移植性,这可能会给你一个比像Robot Framework这样完全便携的解决scheme的优势。 但是,通过使用不同的工具进行开发和testing,您始终可以提高系统的稳定性和可移植性。 因此,在某些情况下,使用基于python的BDD方法testing.Net软件可能是一个好的(甚至更好的)想法。

但是,SpecFlow在日常testing中performance出稳定性和防弹性,包括夜间build造testing等。 此外,它还融入了Microsoftunit testing环境。