WF单元测试系列3:测试Activity的行为
2009-03-10 16:36
381 查看
上一篇中介绍了如何简单地测试Activity的行为,我们只是使用VSTS的单元测试工具,捕获了一个期待的异常,这样简单的行为测试当然是不能满足我们的需求的,我们要进一步测试Activity的更多,更复杂的行为。
比如我们有一个LogActivity 负责写日志,任何调用它的结果等同于调用一个ILog接口的一个Write()方法。我们就要写一个单元测试,测试是否LogActivity的调用等同于对ILog接口的Write()方法的调用。
有的人不禁要问了,这样的测试如何做呢?我们的测试工具只支持Assert操作,难道我要去Check日志文件,看看两个日志是否相等吗?答案是否定的,我们不会这么做,因为这样还属于“状态测试”的范畴,而不是“行为测试”。
还好有人想到了这一点,提供给我们现成的工具,这就是Mock Object Framework。Mock Object Framework基于单元测试中的这样几个问题:
1.在一个庞大的系统中,成百上千个类共同协调完成一个功能。往往一个调用的背后,是几十个类在共同工作,而且很多类的创建很耗费资源和时间。在这样的情况下,运行一个测试的代价可想而知,何况我们每次对代码做一次改动,就要运行单元测试呢。
2.很多代码都需要调用外部资源,比如,读取配置文件,读取数据库,调用远程的Web Service等等。这样的操作很费时间,最主要的是依赖外部的资源。如果外部资源处了问题,测试将无法进行下去。
3.对系统的行为无法准确捕获。一个简单的调用,系统究竟调用了那些类的那个方法,调用了几次,做了几次查询,修改,等等,一系列的操作都无法准确测试,难以保证单元测试的严谨性。
基于以上几点考虑(当然不是全部的),Mock Object Framework出现了。它解决了这些问题,
所以成为了TDD的必备武器!
Mock Object Framework的主要功能:
1.Mock Object---顾名思义,就是模仿对象。正是由于单元测试中庞大的对象群的创建太耗费资源和时间,所以我们使用Mock Object来代替那些对象。Mock Object是轻量级的,代价很低,所以大大减少单元测试的开销。
2.对于一些需要访问外部资源的对象,Mock可以用硬编码代替对外部资源的访问。从而提高效率。
3.Mock Object Framework可以准确地记录所模仿的对象的一切操作,使得我们可以准确地测试对象的行为是否符合要求。
4.Mock Object Framework可以模仿接口,抽象类,或者尚未实现的类,从而加快开发速度。
关于Mock Object Framework,大家可以去找一些资料。
这里我们使用的是Rhino Mock,大家可以去这里看Rhino Mock的简单介绍:http://stephenwalther.com/blog/archive/2008/03/23/tdd-introduction-to-rhino-mocks.aspx
下面开始我们的单元测试:
在前文的工程中,加入一个名叫的Activity,继承自BaseEmployeeActivity。定义三个属性:
public static readonly DependencyProperty
NewEmployeeProperty = DependencyProperty.Register("NewEmployee",
typeof(AcmeEmployee), typeof(StoreNewAcmeEmployee));
public AcmeEmployee NewEmployee
protected override ActivityExecutionStatus Execute(ActivityExecutionContext
executionContext)
我们设定这个Activity的功能是:根据给定的参数,创建一个NewEmployee对象,把它存储在EmployeeDataStore对象中。我们先不实现Execute()方法。注意:EmployeeDataStore会在后面使用依赖注入赋值。
为了测试这个Activity,我们如法炮制,在测试工程中加一个新的Activity:StoreNewAcmeEmployee_Accessor,继承自StoreNewAcmeEmployee。并Wrap它的Execute方法。
接下来添加一个测试类:StoreNewAcmeEmployee_ActivityShould。记得引用Rhino.Mocks库。
先把代码贴出来:
using System;
using AcmeCorp.DomainLibrary.AcmeEmployeeDomain;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NewEmployeeWFLibrary.Activities;
using NewEmployeeWFLibrary_Test.Helpers;
using Rhino.Mocks;//引用Rhino Mock库
namespace NewEmployeeWFLibrary_Test.Activities
在上面的代码中,下面一句比较重要
_mockEmployeeRepository = _mocker.CreateMock<IEmployeeRepository>();
这一句创建了针对接口IEmployeeRepository的Mock对象。注意:在我的工程里,名没有实现这个接口。这就是Mock的特点,可以不用实现功能就能测试。可谓针对接口测试吧!:)
在接下来的测试方法中,首先使用_mocker.Record()记录对Mock对象的操作。在_mocker.Record()的Unsing块中的所有操作,都是我们期待的Mock对象的“行为”。而_mocker.Playback()块则是对Activity的实际测试过程,在这个过程中,必须和Record块对_mockEmployeeRepository的操作一致------也就是说,也调用了它的Add()方法。如果一致,测试通过,否则Rhino Mock会抛出异常,测试失败!
我们运行一下测试,失败!因为还没有为StoreNewAcmeEmployee的Execute()方法添加逻辑。
回过头来实现StoreNewAcmeEmployee的Execute()方法:
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ThrowExceptionOnNullFirstName()
private void ExerciseActivityWithNoExpectations(string firstName, string
lastName, string email,
IEmployeeRepository
employeeRepository)
在这里,没有对Mock对象进行任何记录。就是说不期待对Mock对象进行任何操作。但是期待一个异常:ArgumentNullException,因为传入和一个Null参数。
回到StoreNewAcmeEmployee中,改造Execute()方法为:
protected override ActivityExecutionStatus
Execute(ActivityExecutionContext executionContext)
private void EnsurePropertiesAreValid()
在调用EmployeeDataStore的Add()方法之前,检查参数是否合法,不合法就抛出异常,不调用EmployeeDataStore的Add()方法。
运行测试,通过!
对Activity的行为测试就到这里,下次为大家介绍对Workflow的测试。
附源码:NewEmployeeWF3.rar
注:以上示例来自WF3.5 Hands On Lab,英文好的朋友可以去这里看:https://www.microsoft.com/resources/virtuallabs/step3-msdn.aspx?LabId=c4a993a5-d498-4d5c-9f98-476c1f496d15&BToken=reg
比如我们有一个LogActivity 负责写日志,任何调用它的结果等同于调用一个ILog接口的一个Write()方法。我们就要写一个单元测试,测试是否LogActivity的调用等同于对ILog接口的Write()方法的调用。
有的人不禁要问了,这样的测试如何做呢?我们的测试工具只支持Assert操作,难道我要去Check日志文件,看看两个日志是否相等吗?答案是否定的,我们不会这么做,因为这样还属于“状态测试”的范畴,而不是“行为测试”。
还好有人想到了这一点,提供给我们现成的工具,这就是Mock Object Framework。Mock Object Framework基于单元测试中的这样几个问题:
1.在一个庞大的系统中,成百上千个类共同协调完成一个功能。往往一个调用的背后,是几十个类在共同工作,而且很多类的创建很耗费资源和时间。在这样的情况下,运行一个测试的代价可想而知,何况我们每次对代码做一次改动,就要运行单元测试呢。
2.很多代码都需要调用外部资源,比如,读取配置文件,读取数据库,调用远程的Web Service等等。这样的操作很费时间,最主要的是依赖外部的资源。如果外部资源处了问题,测试将无法进行下去。
3.对系统的行为无法准确捕获。一个简单的调用,系统究竟调用了那些类的那个方法,调用了几次,做了几次查询,修改,等等,一系列的操作都无法准确测试,难以保证单元测试的严谨性。
基于以上几点考虑(当然不是全部的),Mock Object Framework出现了。它解决了这些问题,
所以成为了TDD的必备武器!
Mock Object Framework的主要功能:
1.Mock Object---顾名思义,就是模仿对象。正是由于单元测试中庞大的对象群的创建太耗费资源和时间,所以我们使用Mock Object来代替那些对象。Mock Object是轻量级的,代价很低,所以大大减少单元测试的开销。
2.对于一些需要访问外部资源的对象,Mock可以用硬编码代替对外部资源的访问。从而提高效率。
3.Mock Object Framework可以准确地记录所模仿的对象的一切操作,使得我们可以准确地测试对象的行为是否符合要求。
4.Mock Object Framework可以模仿接口,抽象类,或者尚未实现的类,从而加快开发速度。
关于Mock Object Framework,大家可以去找一些资料。
这里我们使用的是Rhino Mock,大家可以去这里看Rhino Mock的简单介绍:http://stephenwalther.com/blog/archive/2008/03/23/tdd-introduction-to-rhino-mocks.aspx
下面开始我们的单元测试:
在前文的工程中,加入一个名叫的Activity,继承自BaseEmployeeActivity。定义三个属性:
public static readonly DependencyProperty
NewEmployeeProperty = DependencyProperty.Register("NewEmployee",
typeof(AcmeEmployee), typeof(StoreNewAcmeEmployee));
public AcmeEmployee NewEmployee
protected override ActivityExecutionStatus Execute(ActivityExecutionContext
executionContext)
我们设定这个Activity的功能是:根据给定的参数,创建一个NewEmployee对象,把它存储在EmployeeDataStore对象中。我们先不实现Execute()方法。注意:EmployeeDataStore会在后面使用依赖注入赋值。
为了测试这个Activity,我们如法炮制,在测试工程中加一个新的Activity:StoreNewAcmeEmployee_Accessor,继承自StoreNewAcmeEmployee。并Wrap它的Execute方法。
接下来添加一个测试类:StoreNewAcmeEmployee_ActivityShould。记得引用Rhino.Mocks库。
先把代码贴出来:
using System;
using AcmeCorp.DomainLibrary.AcmeEmployeeDomain;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NewEmployeeWFLibrary.Activities;
using NewEmployeeWFLibrary_Test.Helpers;
using Rhino.Mocks;//引用Rhino Mock库
namespace NewEmployeeWFLibrary_Test.Activities
在上面的代码中,下面一句比较重要
_mockEmployeeRepository = _mocker.CreateMock<IEmployeeRepository>();
这一句创建了针对接口IEmployeeRepository的Mock对象。注意:在我的工程里,名没有实现这个接口。这就是Mock的特点,可以不用实现功能就能测试。可谓针对接口测试吧!:)
在接下来的测试方法中,首先使用_mocker.Record()记录对Mock对象的操作。在_mocker.Record()的Unsing块中的所有操作,都是我们期待的Mock对象的“行为”。而_mocker.Playback()块则是对Activity的实际测试过程,在这个过程中,必须和Record块对_mockEmployeeRepository的操作一致------也就是说,也调用了它的Add()方法。如果一致,测试通过,否则Rhino Mock会抛出异常,测试失败!
我们运行一下测试,失败!因为还没有为StoreNewAcmeEmployee的Execute()方法添加逻辑。
回过头来实现StoreNewAcmeEmployee的Execute()方法:
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ThrowExceptionOnNullFirstName()
private void ExerciseActivityWithNoExpectations(string firstName, string
lastName, string email,
IEmployeeRepository
employeeRepository)
在这里,没有对Mock对象进行任何记录。就是说不期待对Mock对象进行任何操作。但是期待一个异常:ArgumentNullException,因为传入和一个Null参数。
回到StoreNewAcmeEmployee中,改造Execute()方法为:
protected override ActivityExecutionStatus
Execute(ActivityExecutionContext executionContext)
private void EnsurePropertiesAreValid()
在调用EmployeeDataStore的Add()方法之前,检查参数是否合法,不合法就抛出异常,不调用EmployeeDataStore的Add()方法。
运行测试,通过!
对Activity的行为测试就到这里,下次为大家介绍对Workflow的测试。
附源码:NewEmployeeWF3.rar
注:以上示例来自WF3.5 Hands On Lab,英文好的朋友可以去这里看:https://www.microsoft.com/resources/virtuallabs/step3-msdn.aspx?LabId=c4a993a5-d498-4d5c-9f98-476c1f496d15&BToken=reg
相关文章推荐
- WF单元测试系列1:测试基本的Activity
- Android官方开发文档Training系列课程中文版:Activity测试之创建单元测试
- WF单元测试系列2:简单测试Activity的行为
- [iOS单元测试系列]Singleton如何测试
- 单元测试系列1——选择测试框架
- Android 中的单元测试(使用 ActivityInstrumentationTestCase2 进行 Activity 测试)
- 玩转Google开源C++单元测试框架Google Test系列(gtest)之五 - 死亡测试
- 单元测试系列一:单元测试之道
- 玩转 Google开源C++单元测试框架Google Test系列(gtest)之五 - 死亡测试(转载)
- 测试系列之一----如何进行单元测试 (转)
- 软件测试系列之四 单元测试(Junit)
- 软件测试实验学习笔记系列3--单元测试
- [Android初级]android单元测试之Activity单独测试
- 单元测试系列之1:开发测试的那些事儿
- 玩转Google开源C++单元测试框架Google Test系列(gtest)之五 - 死亡测试
- 软件测试系列--单元测试
- [iOS单元测试系列]Singleton如何测试
- 单元测试系列之3:测试整合之王Unitils
- Google C++单元测试框架(Gtest)系列教程之三——测试固件(Test fixture)
- 单元测试不是梦,Android+PowerMock系列(1) —— 在Eclipse里搭建测试环境