前言

Moq(英语发音是Mock-you 或者只是mock)是一个针对.Net开发的模拟库,它从开始就完全充分利用了.NET3.5(LINQ表达式树)和C#3.0的新特性(lambda表达式)。
它的目标是让模拟以一种自然的方式与现有单元测试进行集成,使它更加简单、直观,以避免开发人员被迫重写测试或高成本的学习测试框架。这使它成为了一个高生产力、类型安全、重构友好的模拟库。

应用场景

现在越来越多分层架构使用依赖注入的方式来解耦层与层之间的关系,最为常见的就是业务层和数据层之间的依赖注入,业务层不再强依赖数据层对象,而是依赖数据层对象的接口,在IOC容器里面完成依赖的配置。
这种解耦给单元测试带来了极大的便利,使得对业务逻辑的测试可以脱离对数据层的依赖,单元测试的粒度更小,更容易排查出问题所在。

开始Moq

安装Moq

Moq安装非常简单,在Nuget里面搜索moq,第一个结果便是moq框架,点击安装即可。

示例

  • 数据层
1
2
3
4
5
6
7
8
public interface IProductRepository
{
void Add(Product product);

void Delete(string id);

Product Get(string Id);
}
  • 实体
1
2
3
4
5
6
7
8
public class Product
{
public string Id { get; set; }

public string Name { get; set; }

public string Img { get; set; }
}
  • 测试类
1
2
3
4
5
6
7
8
9
10
11
public ProductTest
{
private readonly Mock<IProductRepository> _repository;

public ProductTest()
{
var _mockRepository = new MockRepository(MockBehavior.Default);
_repository = _mockRepository.Create<IProductRepository>();
}
}

  • 测试方法
1
2
3
4
5
6
7
8
9
10
11
[Fact]
public void ShouldReturn_A_Product_If_QueryBy_Id_With_Valid_Parameter()
{
_repository.Setup(x=>x.Get(It.Is<string>(x=>x == "1911112346550025")))
.Returns(new Product(){Name = "双十一"})
.Verifiable();
var product = _repository.Object.Get("1911112346550025");

Assert.NotNull(product);
Assert.True(product.Name.Equals("双十一"));
}

Setup是设定预期值,Id等于1911112346550025就返回Name双十一Product对象。

  • 运行结果
1
2
3
MoqSamples.ProductTest.ShouldReturn_A_Product_If_QueryBy_Id_With_Valid_Parameter
源: ProductTest.cs 行 18
持续时间: 135 毫秒
  • MockBehavior有三种行为

1、Default&emsp;mock行为默认值,就是Loose值
2、Strict&emsp;&emsp;从不抛出异常,如果必要会返回默认值(引用类型为null,值类型为0,或枚举和数组为空)
3、Loose&emsp;&emsp;对象没有合适的设置时,总是抛出异常

如果把Get请求的值1911112346550025改为1911112346550000,测试结果就会报错。

设置为MockBehavior.Strict

  • 运行结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
MoqSamples.ProductTest.ShouldReturn_A_Product_If_QueryBy_Id_With_Valid_Parameter
源: ProductTest.cs 行 18
持续时间: 137 毫秒

消息:
Moq.MockException : IProductRepository.Get("1911112346550000") invocation failed with mock behavior Strict.
All invocations on the mock must have a corresponding setup.
堆栈跟踪:
FailForStrictMock.Handle(Invocation invocation, Mock mock)
IInterceptor.Intercept(Invocation invocation)
Interceptor.Intercept(IInvocation invocation)
AbstractInvocation.Proceed()
IProductRepositoryProxy.Get(String Id)
ProductTest.ShouldReturn_A_Product_If_QueryBy_Id_With_Valid_Parameter() 行 23

设置为MockBehavior.Default或者MockBehavior.Loose

1
2
3
4
5
6
7
8
9
MoqSamples.ProductTest.ShouldReturn_A_Product_If_QueryBy_Id_With_Valid_Parameter
源: ProductTest.cs 行 18
持续时间: 126 毫秒

消息:
Assert.NotNull() Failure
堆栈跟踪:
ProductTest.ShouldReturn_A_Product_If_QueryBy_Id_With_Valid_Parameter() 行 25

结语

不同的行为方式,返回的结果也不同,今天Moq暂时先记到这,下回再聊聊Moq其他功能