(一)使用 NUnit, NUnitAsp, Rhino Mocks 和 Fit 创建BasicSample.Tests

发表于:2009-04-07来源:作者:点击数: 标签:RhinoFitBasicSampleMocksNUnitAsp
我估计你已经能猜到这个项目有什么用途。在文章的第一版本,只是随便讨论了 单元测试 。行业和个人经验都证明:在生产高 质量 产品(这些产品更能适应做改变) 测试驱动开发 是一个关键的因素。而且,使用测试驱动方法往往会生产更好的设计,不好的方面就是会
 我估计你已经能猜到这个项目有什么用途。在文章的第一版本,只是随便讨论了单元测试。行业和个人经验都证明:在生产高质量产品(这些产品更能适应做改变)测试驱动开发是一个关键的因素。而且,使用测试驱动方法往往会生产更好的设计,不好的方面就是会创建许多完美的有效的技术文件。看测试发展中一天的介绍对初学者非常有好处,例如,阅读Kent Beck's的《Test-Driven Development: By Example》。检查示例应用程序的单元测试给人们提供了:应用程序是怎样构建的,有什么可用的性能。当看了单元测试后,我们将进一步研究它们测试的代码。

单元测试的性能

        单元测试必须运行得非常快。如果单元测试组运行得太久,开发者就会停止运行它们,然后我们需要它们一直在运行。事实上,如果测试运行的时间超过了0.1秒,测试可能太慢了。现在,如果你在过去运行过单元测试,你知道任何单元测试(要求访问一个现场数据库)运行的时间会更久。有NUnit时,你可以把测试放在categories里,每次运行测试的不同组时就会更加容易,同时大部分时间就会用在排除测试(此测试与数据库连接)。但是至少,这些“慢”测试应该在Continuous Integration环境里每晚运行。这里有一个已经分类的单元测试的例子。


[TestFixture] [Category("Database Tests")] public class SomeTests { [Test] public void TestSomethingThatDependsOnDb() { ... } }

领域层单元测试

        为了简单化,这个应用程序的领域层很简单,至少可以这样说。但是即使在最简单的领域层,在最小程度上,拥有每一个非异常途径(被领域层覆盖)。在命名空间BasicSample.Tests.Domain里有领域层测试。(一方面,CustomerTests.CanCreateCustomer测试每一个属性的getter/setter时,它是不是非常具有杀伤力?在这个过程中你抓住了几个琐碎的bug)。在阅读这篇文章时,你可能会注意到类型DomainObjectIdSetter.cs;下面将讨论建立这个类型的动机。 
        为了运行单元测试,打开NUnit,转到File/Open Project,打开BasicSample.Tests/bin/Debug/BasicSample.Tests.dll.为了阻止消耗时间的测试继续运行,转到NUnit的Categories t键,双击“数据库测试”和"Web Smoke Tests."此外,点击底部的“删除这些类型”。现在,当你运行单元测试时,只有域名逻辑测试将运行,而且不会被HTTP和数据库访问测试减速。对于这样一个小的应用程序,补充的顶端的“慢”测试是可以忽略的,但是要增加时间来运行更大的应用程序的单元测试。
为数据访问层使用Test Doubles 。

        在进入模拟数据库层之前,要注意这里有一个命名法来描述模拟服务的不同类型。Dummies,fakes, stubs and mocks都用来描述模拟行为的不同类型。对这些区别的总看法引起了一些东西,这些东西将包含在Gerard Meszaros' 即将出版的XUnit Test Patterns里。Meszaros提供了“双重测试”,一般用来描述任何这些行为。Stubs和mocks就是例子代码里显示的这样两个双重测试。

        除非你明确测试DAO类别,通常你不需要运行单元测试,这些单元测试依靠现场数据库。本质上,它们非常慢,而且不稳定。例如:如果数据改变了,测试就崩溃了。当测试域名逻辑时,如果数据库改变,单元测试就不会崩溃。但是主要的障碍就是域名对象它们自己可能依靠DAOs。使用抽象的工厂模式(在例子中存在(以后将讨论))和连接的DAO接口,我们能把DAO双重测试注入到域名对象中,以此来模拟与数据库通信。在CustomerTests.CanGetOrdersOrderedOnDateUsingStubbedDao中包括了一个例子。下面的片段,从单元测试创建了DAO stub,并且通过公共的安装员,把它注入到customer中。因为安装员仅仅希望执行IOrderDao接口,stub DAO就会简单地代替所有现场数据库行为。

Customer customer = new Customer("Acme Anvils"); customer.ID = "ACME"; customer.OrderDao = new OrderDaoStub();

        编写stub DAO 的另一个选择,可以静态地大量生成“无需实现接口”的代码,而这些可以通过如Rhino Mocks 或NMock工具模拟DAO。无论哪个都是非常好的选择,但是Rhino Mocks 以一种强而有力的方式调用方法,而不是使用数字符,而NMock 就是使用数字符。这使得能检查它的使用编译时间,协助重命名属性和方法。演示示例CustomerTests.CanGetOrdersOrderedOnDateUsingMockedDao 告诉人们使用Rhino Mocks 3.0来创建模拟的IOrderDao 。尽管看起来建立一个模拟的对象比建立一个stub更复杂。补充的灵活性和很大程度上减少的“没有执行”代码是有力的好处。下面的代码,在类型MockOrderDaoFactory.cs中找到的,展示了IOrderDao是怎样和Rhino Mocks一起模拟的。本质上,它建立了一个“静态”模拟,或者一个stub,事实上,在变元中经过了什么并不重要:它总是返回同一个例子命令(由TestOrdersFactory创建)。但是和Rhino Mocks模拟并没有限制在dumb reflexes上,例如:这个可以像要求的一样有响应。
public IOrderDao CreateMockOrderDao() { MockRepository mocks = new MockRepository(); IOrderDao mockedOrderDao = mocks.CreateMock<IOrderDao>(); Expect.Call(mockedOrderDao.GetByExample(null)).IgnoreArguments() .Return(new TestOrdersFactory().CreateOrders()); mocks.Replay(mockedOrderDao); return mockedOrderDao; }

       

原文转自:http://www.ltesting.net