背景
使用 moq 来 mock 依赖写单元测试的时候,有时候会有一些依赖会比较多的服务,构造函数爆炸,但是实际测试的方法或者逻辑可能只用到了一两个依赖,这个时候可以借助于 Moq.AutoMock 来简化代码,不用每个依赖都给一个初始化值了,来看看下面的示例吧!
例子
首先我们准备几个要测试的方法:
public interface I0
{
int GetResult();
}
public interface IA : I0
{
}
public interface IB : I0
{
}
public interface IC : I0
{
}
public interface ID : I0
{
}
file sealed class TestService
{
private readonly IA _a;
private readonly IB _b;
private readonly IC _c;
private readonly ID _d;
// 堆代码 duidaima.com
public TestService(IA a, IB b, IC c, ID d)
{
_a = a;
_b = b;
_c = c;
_d = d;
}
public int TestMethod1()
{
return _a.GetResult();
}
public int TestMethod2()
{
return _b.GetResult() + _c.GetResult();
}
public int TestMethod3()
{
return _d.GetResult() + 100;
}
}
TestService 中的三个测试方法就是我们要测试的对象,以第一个方法为例子,我们之前测试的话一般会怎么样去写呢,大概类似下面这样,new 一个 TestService 并将参数都 mock 填进去。
[Fact]
public void TestMethod1WithoutAutoMocker()
{
var mockA = new Mock<IA>();
var service = new TestService(mockA.Object, new Mock<IB>().Object, new Mock<IC>().Object, new Mock<ID>().Object);
mockA.Setup(x => x.GetResult())
.Returns(1);
var result = service.TestMethod1();
Assert.Equal(1, result);
}
看方法的实现可以知道,我们 TestMethod1 这个方法只和 IA service 有关系,和其他的依赖服务并没有关系,但还是得传其他的参数来获得一个 TestService 实例。使用 Moq.AutoMock 之后就可以比较方便地获取实例了,我们可以使用 GetMock<IA> 来获取一个 Mock<IA> 对象并基于此设置 mock 行为。
改造后的代码如下:
[Fact]
public void TestMethod1WithAutoMocker()
{
var autoMocker = new AutoMocker();
autoMocker.GetMock<IA>().Setup(x => x.GetResult())
.Returns(1);
var service = autoMocker.CreateInstance<TestService>();
var result = service.TestMethod1();
Assert.Equal(1, result);
}
当然写法不止这一种,再看下后面两个测试方法
[Fact]
public void TestMethod2()
{
var autoMocker = new AutoMocker();
autoMocker.GetMock<IB>().Setup(x => x.GetResult())
.Returns(1);
var cMock = new Mock<IC>();
cMock.Setup(x => x.GetResult())
.Returns(1);
autoMocker.Use(cMock);
var service = autoMocker.CreateInstance<TestService>();
var result = service.TestMethod2();
Assert.Equal(2, result);
autoMocker.VerifyAll();
}
TestMethod2 会用到 IB/IC,我们这里设置这两个,这里的 IB 是通过 autoMocker.GetMock<IB>() 的方式来获取设置,和前面的方式一致,IC 则是我们自己通过 new Mock<IC> 来获取,之后通过 autoMocker.Use(cMock) 来注入到 autoMocker 中。
autoMocker.Use 方法除了可以设置 mock 对象之外,也可以直接注入某个服务实例,比如我们也可以使用 autoMocker.Use(cMock.Object) 来注入 IC 服务。其他的和之前一样,最后调用了 VerifyAll 的方法来验证我们 mock 的设置都被调用。
再来看最后一个测试方法:
[Fact]
public void TestMethod3()
{
var autoMocker = new AutoMocker();
autoMocker.Use<ID>(x=> x.GetResult() == 1);
var service = autoMocker.CreateInstance<TestService>();
var result = service.TestMethod3();
Assert.Equal(101, result);
autoMocker.GetMock<ID>()
.Verify(x => x.GetResult());
}
这次我们使用 autoMocker.Use<ID>(x => x.GetResult() == 1) 来设置 ID 的 GetResult 方法返回 1, 然后在最后通过 GetMock<ID> 获取 mock 对象并验证 GetResult 的方法调用。
用了 Moq.AutoMock 之后是不是简单了一些呢,希望对大家有所帮助~~