SpecFlow http://blog.csdn.net/yujunwu2525/article/details/7839859

将业务需求与.NET代码结合起来

SpecFlow旨在弥合领域专家与开发人员之间的代沟,通过结合可读性高的行为规格与例子(一些规范)进行底层实现。

我们的目标是提供一个高效实用的方法来完成我们对.NET项目的实例化需求 (①Specification-By-Example ),SpecFlow同样也适用于ATDD与BDD这些与实例化需求类似的概念。

SpecFlow是开源的,你可以在这里找到它的一些信息: BSD license

作为‘小黄瓜’家族的一部分,SpecFlow使用了Cucumber官方的解析器,同时能集成在.NET framework, Silverlight, Windows Phone and Mono这些环境中,你可以在这些框架上使用它。

下载与入门

怎样开始

  1. 第一步

    - 用规范的DSL(domain specific language领域语言)来描述行为、业务与可读性高的'测试'语句:

  2. 第二部

    -自动生成场景,并不断的根据测试文档(的内容)来修改测试直至通过。

想要了解的更多:

探索SpecFlow家族的更多内容

帮助我们检查 SpecFlow 将使它变的更好

  • SpecRun - 一款适用于SpecFlow的只能集成测试工具
  • SpecLog - 一款基于SpecFlow测试的日志工具
  • 想让你的开源工具显示在这里吗? 联系我们吧:Contact us!

重构:SpecFlow的Logo与网站

随着 SpecFlow 1.9 的更新,我们正在重新设计SpecFlow logo.

我们正在重构我们的网站与logo,这是一个1.9版本的发布网站,新网站出现之前我们将暂时使用这个主页。

(注:

①实例化需求是一种使用基于图表举例说明之类的具象信息来协助项目小组进行需求定义与面向企业级功能测试的方法,而不是依靠一些抽象方法来说明。实例化需求是一组方法,它以一种对开发团队有所帮助的方式(理想情况下表现为可执行的测试)描述计算机系统的功能和行为,让不懂技术的利益相关者也可以理解,即使客户的需求在不断变化,它也具有很好的可维护性,可以保持需求的相关性。)

2012-08-06 17:32 256人阅读 评论(0) 收藏 举报
 

目录(?)[+]

 

原文:

Behavior-Driven Development with NBehave

这里模拟了一个“银行账户”的类

一个余额属性,一个存款方法,一个撤销账户的方法,一个转账的方法。
  1. public sealed class Account
  2. {
  3. private int balance;
  4. public int Balance
  5. {
  6. get { return balance; }
  7. set { balance = value; }
  8. }
  9. public void Deposit(int amount)
  10. {
  11. }
  12. public void Withdraw(int amount)
  13. {
  14. }
  15. public static void Transfer(Account from, Account to, int amount)
  16. {
  17. }
  18. }

初始化测试类(注:引用了NBehave的dll

  1. public class AccountTest : SpecBase
  2. {
  3. public Account account;
  4. public Account account2;
  5. [SetUp]
  6. public void Initialize_before_each_test()
  7. {
  8. account = new Account();
  9. account2 = new Account { Balance = 100 };
  10. }
  11. }
 

Stories

(关于BDD)所有的内容都从一个‘故事’(Story)开始。

很早之前,NBehave的开发者们就开始尝试写出一个叫Story的类,这个类被设计出来的意义就是为了能够描述一组特定的应用场景,以用来模拟我们想要具体测试的业务。

举个例子,假设我们把这个story描述成一个存款业务,于是:

  1. [Story, That, Should("Increase account balance when money is deposited")]
  2. public void Deposit_should_increase_account_balance()
  3. {
  4. Story story = new Story("Deposit");
  5. story.AsA("User")
  6. .IWant("The bank account balance to increase by the amount deposited")
  7. .SoThat("I can deposit money");
  8. // scenarios here
  9. }

(注:这个story从上往下读,按字符串的描述可以读成:

存款业务应该增加账户余额

作为一个用户,我想要让账户内的余额增加,那么我可以选择通过存款实现

PS:我的语序混乱了,但原文的例子是这样写的

整段代码其实并没有技术实现上的实际意义,它只不过是利用了一种类似第三方单元测试的语法描述了业务的特性(原文:the attributes that decorate the test method are really using the familiar xUnit testing attributes 而对于上个story,文章使用了NBehave),对于BDD而言,其目的是为了让整个业务的逻辑性和可读性增强,当然我们也可以换成别的语法风格,比如

  1. using That = MbUnit.Framework.TestAttribute;
  2. using Describe = MbUnit.Framework.CategoryAttribute;
  3. using For = MbUnit.Framework.CategoryAttribute;
  4. using Wrote = MbUnit.Framework.DescriptionAttribute;
  5. using Should = MbUnit.Framework.DescriptionAttribute;

其实不论那种风格,目的都是为了让业务和逻辑变的可读,而NBehave的风格已经成为了一种趋势,因为如上面所演示的一样,他的可读性太棒了。

现在回到那个story里,发现没有,整个story是没有任何test存在的,它仅仅是使用了文字与NBehave提供的接口就完成了一个完整业务的描述。

于是story的定义出来了,他就是为了更清晰的反映出一个特定需求(注意,是一个特定的需求,而不是一个无序的故事,在实际的开发中,这需要业务人员悉心的分离出来),然后当这个story送到了开发人员的手上时,他们就能有针对性的并能更容易决定写出哪些测试。

Scenarios

我们有了一个story,现在可以开始写测试了。

我们根据story,然后提供一些叫做scenario的内容-描述一个可能发生的事件(原文:we have to provide something called a scenario – a description of one possible thing that can happen)

就银行存款案例而言,可能发生这些情况:你的账户能正常存款(这最好);你的账户被注销了(这当然就不能存款了);或者你输入了一个负的数字(一般人当然不会);我们要对每一种可能出现的场景做到覆盖率100%的测试,让我们先从简单的开始:

  1. story.WithScenario("Money deposit")
  2. .Given("My bank account is empty", () => { account.Balance = 0; })
  3. .When("I deposit 100 units", () => account.Deposit(100))
  4. .Then("The account balance should be 100", () => account.Balance.ShouldEqual(100));

这个小片段大概已经告诉我们什么是BDD了,是的,一个前提,一个后置条件,最后是结果

  • 首先, WithScenario() 告诉系统这是一个什么样的scenario。这段信息将被设置到测试工具中,之后的所有(测试)内容都请遵循这个场景的限定。
  • 然后, 使用 Given() 来定义一个前提-即初始化一个空的账户。这里有两个参数,一个 string 用来描述你将做什么, 另一个 Action 将实现你之前的定义(即给定/初始化条件)。
  • When() 方法描述一个行为,当一个行为动作发生时该怎样处理的,这就是我们之后想要测试的内容。
  • 最后, Then() 方法将对我们的方法进行测试。
(通顺的翻译:
在“存款”这个场景中,
我们有一个余额为0的账户,
现在我往账户里存入100元,
于是我的账户余额应该变成了100)

你可能已经发现了,在上面的测试中,包含了大量了lambda表达式,这是NBehave的特点之一,可以灵活的配置每一个Action。

现在来看看测试结果(注:文章中使用的是MbUnit,写出这个测试的主要目的是让大家可以很清晰的看到NBehave所带来的好处——结构清晰,通俗易懂

  1. *** DebugTrace ***
  2. Story: Deposit
  3. Narrative:
  4. As a User
  5. I want The bank account balance to increase by the amount deposited
  6. So that I can deposit money
  7. Scenario 1: Money deposit
  8. Given My bank account is empty
  9. When I deposit 100 units
  10. Then The account balance should be 100 - FAILED
  11. MbUnit.Core.Exceptions.NotEqualAssertionException:
  12. Equal assertion failed: [[0]]!=[[100]]

很显然,因为我们根本没有实现Deposit方法,测试当然不能通过,现在我们想写办法(添加一些内容)来让测试通过。

  1. public void Deposit(int amount)
  2. {
  3. balance += amount;
  4. }

测试通过了,瞧,多简单!通过业务分析,描述场景以及测试,我们实现了一个业务方法了。

再看一个复杂点的,在存款时尝试存一个负的数

  1. story.WithScenario("Negative amount deposit")
  2. .Given("My bank account is empty", () => { account.Balance = 0; })
  3. .When("I try to deposit a negative amount", () => { })
  4. .Then("I get an exception",
  5. () => typeof(Exception).ShouldBeThrownBy(() => account.Deposit(-100)))
  6. .And("My bank account balance is unchanged",
  7. () => account.Balance.ShouldEqual(0));

这里注意两件事,一是在Then()中使用扩展方法ShouldBeThrownBy(),确保调用了 Deposit()方法添加一个负数,另一个是使用And来验证余额(注:And相当于重载了一次Then()方法,它在使用在Given(),When()的后面时,是同样的意义,表示重载上一个方法

(关于为什么使用 When("I try to deposit a negative amount", () => { })这样一个空方法,作者的解释大概是说想要通过这样一个不合理的方法来捕捉到一个异常,具体不翻译了,和NBehave关系不大,是他例子中的一种想法)

跑一遍测试,然后看输出:

  1. *** DebugTrace ***
  2. Story: Deposit
  3. Narrative:
  4. As a User
  5. I want The bank account balance to increase by the amount deposited
  6. So that I can deposit money
  7. Scenario 1: Money deposit
  8. Given My bank account is empty
  9. When I deposit 100 units
  10. Then The account balance should be 100
  11. Scenario 2: Negative amount deposit
  12. Given My bank account is empty
  13. When I try to deposit a negative amount
  14. Then I get an exception - FAILED

我们得到了一个异常,第二个场景没有通过,于是我们继续想办法来让测试通过:

  1. public void Deposit(int amount)
  2. {
  3. if (amount <= 0)
  4. throw new Exception();
  5. balance += amount;
  6. }

同样是一个很简单的方法,继续通过分析,举例场景以及测试,又完成了一个业务需求。

上面那个场景的语法很明显是不易懂的,不但使用了一个扩展方法,并且还没有一个行为发生(空的When()方法),很不合逻辑

更改一下场景:

  1. story.WithScenario("Valid transfer")
  2. .Given("I have 100 dollars", () => { account.Balance = 100; })
  3. .And("You have 100 dollars", () => { account2.Balance = 100; })
  4. .When("I give you 50 dollars",
  5. () => Assert.DoesNotThrow(() => Account.Transfer(account, account2, 50)))
  6. .Then("I have 50 dollars left", () => account.Balance.ShouldEqual(50))
  7. .And("You have 150 dollars", () => account2.Balance.ShouldEqual(150));

整个例子完成了,没有扩展方法,没有奇怪的语句,整个测试和实现就是由语句和NBehave提供的接口来完成的。

.NET的DTO映射工具AutoMapper

原文:https://github.com/AutoMapper/AutoMapper/wiki/Getting-started

参考:http://www.infoq.com/cn/news/2010/02/automapper-rtw/

说明:这是一款DTO的映射工具,如果你不了解DTO,可能会不理解它到底有什么意义。

概述:

AutoMapper是基于对象到对象约定的映射工具,它可以把复杂的对象模型转为DTO,或者其他的--那些让设计更合理更适于序列化、通信、传递消息的简单对象或者干脆就只是在领域层与应用层之间搭建一个简单的ACL防护层(就像DTO一样,用于代码的显示转换)来增加各自层的相互独立性。

一般用于ViewModel模式和跨服务范畴。

AutoMapper包含以下功能:

简介:

AutoMapper是什么?

AutoMapper是一个将对象映射到对象的映射工具。对象映射的工作机制是:通过输入一个类型的对象然后将其转换成另一个不同类型的对象并输出。在这里AutoMapper要做的(所擅长的)就是提供一些方便的方法,把那些麻烦累人的工作(指对象转换)从类型转换算法中剔除掉。只要需要转换的类型遵循了AutoMapper制定的规则,那么你基本上就不用再写配置算法来实现两个类型之间转换的工作了,AutoMapper会替你自动完成。

//原文:把工作从如将类型A转换成类型B的映射算法中剔除,如果类型B遵循了AutoMapper制定的规则,那么你基本上就可以以零配置来实现两个类型之间转换的工作了。( What makes AutoMapper interesting is that itprovides some interesting conventions to take the dirty work out of figuringout how to map type A to type B.)

为什么使用AutoMapper?

写类型转换的映射代码实在一件枯燥的事情,而对映射做测试则更乏味。AutoMapper为类型转换提供了非常简单的配置算法,同样也易于测试。

现在一个现实的问题是,“为什么使用对象到对象的映射?”

类似于‘映射转换’这样的情况将会发生在项目中非常多的地方,尽管大部分只会发生在层与层之间的边界,例如表示层与领域层之间,或者服务层与领域层之间。如果层与层之间有相关联的话,那么一层的变化会影响到另一层,因此,对象到对象的映射可以隔离这些层的模型,让每一层的变的更加独立,每一层的变化只会影响到自身。

怎样使用AutoMapper?

首先,你需要一个源数据对象和一个目的数据对象。在设计目的数据对象时,会受到它所在层的影响(即在转换对象时你需要getXXX,setXXX一堆方法),但是对于AutoMapper而言,它最大的优势就是只要你在设计目的数据对象时,让其中的成员(按一定规则)去匹配源数据对象成员的命名方法,那么这些问题就能很好解决。

例如:一个源数据对象中有一个属性叫:“FirstName”,那么它就会自动映射到目的数据中另一个叫“FirstName”的属性。

也就是说,你只需要有两个对象,然后引用AutoMapper,那么你将能轻松的创建对象之间的映射

比如:

  1. Mapper.CreateMap<Order,OrderDto>();

CreateMap方法类型左边的是源数据,右边的是目的数据,在执行完这条映射语句后,就可以使用Map方法了。

  1. OrderDtodto = Mapper.Map<Order, OrderDto>(order);

AutoMapper也有非泛型的方法,不过对于那些情况,你可能不好判断编译时的类型。

AutoMapper该在什么地方配置?

如果你使用静态的映射方法,配置时只需要在appDomain中配置一次,这意味着你最好的选择就是把配置代码方法在程序启动项中,例如Global.asax。需要说明的是,在bootstrapper(启动加载器)内配置自己,这个bootstrapper也被称作为启动方法。

如何测试一个映射?

创建一个映射测试,你要做两件事

·        调用启动项中创建映射的方法

·        调用Mapper.AssertConfigurationIsValid方法

就像这个例子一样

  1. AutoMapperConfiguration.Configure();
  2. Mapper.AssertConfigurationIsValid();

SpecFlow的更多相关文章

  1. SpecFlow教程--快速入门

    原文http://www.specflow.org/getting-started/ 一.安装 为了能正确安装SpecFlow所需要的东西,你必须安装集成IDE的插件以及设置你的项目使用SpecFlo ...

  2. 怎么用C#获取Scenario step在specflow里

    公司最近在用specflow 这种BDD的模式,但PM还是想把case再存进TestManager里面一份儿一遍后期集成TestManager 自动runcase用.所以我们需要获取每个scenari ...

  3. SpecFlow - Cucumber for .NET

    SpecFlow使用入门 SpecFlow是一个BDD工具,在这里对BDD不多赘述,你可以阅读一下微软2010年十二月的一篇文章,此外如果你想要更多了解SpecFlow,可以参考我的另一篇翻译(当然, ...

  4. SpecFlow使用入门之C# BDD

    SpecFlow使用入门 http://www.specflow.org/ SpecFlow是一个BDD工具,在这里对BDD不多赘述,你可以阅读一下微软2010年十二月的一篇文章,此外如果你想要更多了 ...

  5. Acceptance Test - Business Readable Acceptance Test using - Specflow

    Agenda Benefits Living document Frameworks specflow supports How to integrate it in development cycl ...

  6. BDD实战篇 - .NET Core里跑Specflow - 可以跑集成测试和单元测试

    这是<如何用ABP框架快速完成项目 >系列中和DevOps系列文章其中一篇文章.   BDD很赞!比TDD先进很多,能够大大提高编码效率.   上一篇文章说了如何在.NET Core里安装 ...

  7. BDD实战篇 - 在.NET Core下安装Specflow

    这是<如何用ABP框架快速完成项目 >系列中的一篇文章. BDD很赞!比TDD先进很多,能够大大提高编码效率. 让我们动手起来吧!先在.NET Core下安装Specflow! 官网教程在 ...

  8. 通过反编译让SpecFlow支持多层属性值的验证

    需求:在使用SpecFlow时,我希望能对目标对象所关联的对象属性进行验证,但SpecFlow(Version 1.9.0)无法实现.如图中红框,可以对专户所属的金融机构的名称进行验证. 反编译步骤 ...

  9. [转]SpecFlow使用入门

    SpecFlow是一个BDD工具,在这里对BDD不多赘述,你可以阅读一下微软2010年十二月的一篇文章,此外如果你想要更多了解SpecFlow,可以参考我的另一篇翻译(当然,也可以直接进入官网) 一. ...

随机推荐

  1. 【ORACLE】使用数据泵的生产环境impd,expdp数据迁移

    ********************************************** ** 原文: blog.csdn.net/clark_xu  徐长亮专栏 **************** ...

  2. 探测器 C++ Singleton(辛格尔顿)

    一.静态模式不是单一的情况下, 刚开始学习的人可能误, 误以为所有的成员变量和成员方法用于 static , 就是单例模式了: class Singleton { public:     /* sta ...

  3. Android开发之自己主动登录功能的实现

    在我们平时使用的手机应用都能够实现仅仅须要登陆一次账号后,第二次进入应用直接跳转到效果界面的效果,还有QQ的登陆框是怎样记忆我们的隐身登陆,保存账号选项的呢,这些都是通过使用SharedPrefere ...

  4. JavaScript--基于对象的脚本语言学习笔记(三)

    事件处理器 1.一个数据校验表单的例程 <html> <head> <title>js练习</title> <meta http-equiv=&q ...

  5. NYNU_省赛选拔题(8)

    题目描述 一天萌萌哒孟孟学长去博物馆参观,他想看到更多的东西.博物馆可以表示为N × M细胞的一个矩形区域. “.”表示为路,“*”表示为墙壁,每个墙壁上面都挂有美丽的画卷.孟孟学长可以看到与他所在位 ...

  6. Unity + iBatis + Asp.net Mvc 系统搭建

    Unity + iBatis + Asp.net Mvc 系统搭建 之前用EntityFramework Code First做了一些小项目,很是方便:后来在一个 Java 项目中接触了myBatis ...

  7. DFS-leetcode Combination Sum I/I I

    深度优先搜索(DFS)它是一个搜索算法.第一次接触DFS它应该是一个二进制树的遍历内部,二叉树预订.序和后序实际上属于深度遍历-first.在本质上,深度优先搜索,遍历中则看到了更纯正的深度优先搜索算 ...

  8. Corel VideoStudio Pro X7(会声会影)

    今天了解一天的视频剪辑方面的知识,自己也动手做了一个. 好啦!下面给大家一些建议: 剪辑软件选择: 1.易学易用.容易上手.模板丰富:会声会影:(需要安装包的可以留言和私信我)2.功能齐全.占用资源少 ...

  9. Java有用的经验--Swing片

    Java有用经验总结--Swing篇 前言 本文前言部分为我的一些感想,假设你仅仅对本文介绍的Java有用技巧感兴趣,能够跳过前言直接看正文的内容. 本文的写作动机来源于近期接给人家帮忙写的一个小程序 ...

  10. C#创建和初始化类

    using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace C_编辑 ...