一. 预备知识
单元测试:单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证
Mock测试:在测试过程中,对于某些不容易构造或者 不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法
Lambda表达式
Linq(Language INtegrated Query)
二. 初识Moq
Moq(读作“Mock-you”或“Mock”)是.net平台下最有生产力,类型安全和重构友好的mocking库。
在进行单元测试的时候,经常会碰到这些情景:
1.想要对业务逻辑层的某个AService方法进行单元测试,而该方法内部却调用了数据链路层的ADataAccess方法,这时候的单元测试,其实已经破坏了单元测试功能点单一的原则,因为AService方法的单元测试首先需要ADataAccesss正常运行。更有甚者,这时候测试环境根本无法连接数据库。
2.想要对MVC的 Controller层的某AProcess方法进行单元测试,但是该方法内部调用了HttpContext的相关属性或方法。若要进行单元测试,光是模拟Web环境就要花费大量时间。
而Moq就我们提供了模拟真实对象行为的能力,然后交给被测试功能使用,以此判断被测试功能是否正确。
注意:Moq只能模拟接口或抽象类。
三. Simple Demo
public interface ITalk
{//简单接口
string Talk();
}
public abstract class Person
{//简单抽象类
public abstract bool IsTalk();
}
class Program
{
static void Main(string[] args)
{
//简单的模拟接口
var iTalk = new Mock<ITalk>();
iTalk.Setup(i => i.Talk()).Returns("Hello,I'm Mock");
Console.WriteLine(iTalk.Object.Talk());
//若该行代码放到iTalk.Object.Talk(),则会引发异常,因为Talk()没有被调用过
iTalk.Verify(i => i.Talk(), Times.AtLeastOnce());
//简单的模拟抽象类
var aPerson = new Mock<Person>();
aPerson.Setup(a=>a.IsTalk()).Returns(false);
Console.WriteLine(aPerson.Object.IsTalk());
Console.ReadLine();
}
}
F5运行后,效果如下
上边的例子可以看到,完全没有实现过的接口或者抽象类,可以通过Moq模拟一个虚拟的对象
四. Moq的用法
Moq中最常用的类分别是
1.Mock<T> 提供一个模拟实现了T接口(或抽象类)的对象
名称 |
描述 |
|
常用方法 |
Setup (Expression<Action<T>>) |
核心方法,通过Lambda设置模拟对象的方法 |
核心方法,验证模拟对象方法是否(按照规定的传入参数)执行并通过。该方法有多个重载,分别验证方法执行的次数,失败时的错误消息提示 |
||
属性 |
Object |
模拟对象的实例 |
2.It 为了使Moq模拟的方法根据不同的传入参数而有不同的行为,Moq提供了该类,可以指定一个传入参数的匹配条件。
名称 |
描述 |
|
常 用 方 法 |
匹配给定TValue类型的任何值 |
|
IsInRange<TValue> |
匹配指定范围的任何值 |
|
匹配符合给定正则表达式模式的字符串 |
五. Moq实战
回到刚最开始时提到的情景1.
即数据库访问层没有实现,如何测试业务逻辑层的某个方法是否正确。
模拟代码如下:
文件1. DataModel
public class DataModel
{
public string Id { get; set; }
}
文件2.IDataAccess
public interface IDataAccess
{
List<DataModel> GetDataModel();
}
文件3IDataModelService
public interface IDataModelService
{
int GetCount();
}
文件4DataAccess
public class DataAccess : IDataAccess
{
public List<DataModel> GetDataModel()
{
return null;
}
}
文件5DataModelService
public class DataModelService : IDataModelService
{
private IDataAccess iDataAccess;
public DataModelService()
{
iDataAccess = new DataAccess();
}
public DataModelService(IDataAccess iData)
{
iDataAccess = iData;
}
public int GetCount()
{
return iDataAccess.GetDataModel().Count;
}
}
测试文件 public class Test
{
static void Main(string[] args)
{
//模拟数据
List<DataModel> lisData = new List<DataModel>() { new DataModel { Id = "1" }, new DataModel { Id = "2" } };
//模拟对象
var data = new Mock<IDataAccess>();
data.Setup(d => d.GetDataModel()).Returns(lisData);
//测试方法
DataModelService dataService = new DataModelService(data.Object);
Console.WriteLine(dataService.GetCount());
Console.ReadLine();
}
}
运行结果截图:
情景2:
有时间做补
六.资料
Moq下载:http://code.google.com/p/moq/ 内含帮助文档
Moq入门:http://www.codethinked.com/post/2009/03/08/Beginning-Mocking-With-Moq-3-Part-1.aspx
http://www.codethinked.com/post/2009/03/10/Beginning-Mocking-With-Moq-3-Part-2.aspx
http://www.codethinked.com/post/2009/03/10/Beginning-Mocking-With-Moq-3-Part-3.aspx
http://www.codethinked.com/post/2009/03/10/Beginning-Mocking-With-Moq-3-Part-4.aspx