第七节:使用Mock对象

7.1简单的替换  

通常那些大腕明星都有自己的替身,那么对于单元测试来说有时候我们也需要自己的替身,比如我们并不像在真正的数据库上做测试。

假设我们在代码中调用Now属性来返回系统当前的日期和时间

View Code
 public DateTime Now
{
get {
return DateTime.Now;
}
}

(一般而言,我们通常建议对应用程序范围外的功能进行包装,从而能更好的封装他们)

上面的代码中我们把当前时间包装在我们自己写的代码中,因此调试就容易了些

View Code
public DateTime Now
{
get {
if (DEBUG)
return currentTime;
else
return DateTime.Now;
}
}

首先当只有在代码全部调用这个封装的Now,这个方法才有效,但是我们需要一种更加干净,更加面向对象化,同时可以实现相同功能的方法。

7.2Mock对象

有一种测试模式可以帮助我们 ,就是mock对象,mock对象也就是真是对象在调试期的替代品。

使用它来测试一共有三个关键步骤:

1 使用一个接口来描述这个对象

2 为产品代码实现这个接口

3以测试为目的,在mock对象中实现这个接口

因为被测试代码只会通过接口来引用对象,所以它就不知道引用的真实性了

我们对相面的代码改写

接口

View Code
 public  interface Environmental
{
DateTime Now
{
get;
}
//其他的方法都省略了
}

接下来我们编写真实的代码

View Code
  public class MocksystemEnvironment:Environmental
{
private DateTime currenttime;
public MocksystemEnvironment(DateTime when)
{
currenttime = when;
}
public DateTime Now
{
get
{
return currenttime;
}
}
public void IncrementMinutes(int minutes)
{
currenttime = currenttime.AddMinutes(minutes);
}
}

注意我们给构造函数传入了一个时间参数,用作初始的当前时间制,并且构造函数用一个实例变量把它保存了下来,这个变量currenttime就是我们请求当前时间时所要返回的值,同事我们还提供了另一个方法,它会给这个时间加上给定的分钟数,这让你能够控制mock对象返回的日期和时间。

6.3正规化Mock Object

在。net中有多款Mock框架提供选择(http://www.mockobjects.org找到很多)这里我们看其中的一款DotNetMock

DotNetMock框架实际上是一个三合一

1.一个框架,让你有组织的创建mock对象

2.包含一小套预先定义的mock对象,可以拿来测试你的程序

3.还有一项技术,可以动态创建mock对象。

DotNetMock框架

对它了解之后,你会发现创建一个mock对象很简单,只不过是一些代码实现一个特殊的接口,返回你需要他返回的值

 

为了掩饰这一点,让我们看看如何使用DotNetMock框架来帮我们测试一个访问控制库,我们将从一个简单的类AccessControler开始,每个AcsessController对象负责控制一种资源,我们将把资源的名字传给构造函数,为了决定一个用户是否使用对象的资源, 我们调用对象的CanAccess函数传入用户名的名字和密码,这个类如下

View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
class AccessController
{
private Ilonger longer;
private string resource;
public AccessController(string resource, Ilonger longer)
{
this.longer = longer;
this.resource = resource;
longer.Setname("AccessControl");
}
public bool CanAccess(string user, string password)
{
longer.Log("Checking acces for"+user+"to"+resource);
if (password.Length == 0 || password == null)
{
longer.Log("missing password,Access denied");
return false;
}
//more checks
longer.Log("Access granted");
return true;
}
}
}

longer接口

View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
interface Ilonger
{
void Setname(string name);
void Log(string msg);
}
}

我们可以很容易的创建接口mock对象,仅仅需要两个函数,一个用于设置正在longing的内容和名字,另一个用来log实际的消息

View Code
 public class MockLoger1 : Ilonger
{
public void SetName(string name) { }
public void Log(string msg) { }
}

有了这些我们可以编写基本的单元测试了,在第一个测试中,将检测access controller是否在我们没有传入密码的情况下正确的拒绝了访问,

View Code
[TestFixture]
public class testAccessController
{
[Test]
public void MissingPassword()
{
MockLoger1 longer = new MockLoger1();
AccessController access = new AccessController("secrets",longer);
Assert.IsFalse(access.CanAccess("dave",null));
Assert.IsFalse(access.CanAccess("dave",""));
}
}

然而这个测试并没有验证我们的access controller 是否logging 了正确的消息,这就是mock能帮得上忙的地方,






posted @ 2012-02-21 22:14  Jimmy-Lee  阅读(517)  评论(0)    收藏  举报