上一章介绍了手动创建单元测试依赖,和Moq
的小demo
这一章我们看一下Moq
的使用。
不过,at the vary beginning,我想再和大家确认两个概念问题——Stub
(存根)和Mock
(模拟)
Stub
和Mock
都是测试方法依赖隔离的伪造对象
,不同之处是Stub
是测试方法运行所需要的依赖,
Mock
是测试方法验证说需要的依赖。
听懂了么?没有懂对不对。没关系,我们上代码。
public class Foo2
{
private ILog _log1;
private ILog _log2;
public Foo2(ILog log1,ILog log2)
{
_log1 = log1;
_log2 = log2;
}
public void DoB()
{
//do something
var text = _log1.Read();
_log2.Write(text);
}
}
还是之前的日志系统的例子,不过这次我们做的是从日志系统1中读取内容,存入到日志系统2中。
看一下我们的测试代码
[TestMethod]
public void TestMethod3()
{
var fakeLog1 = new Mock<ILog>();
var fakeLog2 = new Mock<ILog>();
fakeLog1.Setup(log => log.Read()).Returns("I'm slim");
var foo = new Foo2(fakeLog1.Object,fakeLog2.Object);
foo.DoB();
fakeLog2.Verify(log => log.Write("I'm slim"));
}
解释一下fakeLog1.Setup(log => log.Read()).Returns("I'm slim");
是设置fakeLog1
在调用Read
方法时,一定会返回"I'm slim"
,
而 fakeLog2.Verify(log => log.Write("I'm slim"));
则是验证fakeLog2
是否被调用了Write
方法,并且传入的参数是"I'm slim"
这个时候我们说fakeLog1
是Stub
,而fakeLog2
是Mock
Stub
在Moq
中对应方法的是Setup
,Mock
对应的方法是Verify
。
英文好的同学可以参见https://martinfowler.com/articles/mocksArentStubs.html
Martin Fowler大大对这两个概念有比较明确的解释。
请注意这里的Mock
和Moq
框架中的Mock<T>
不是一个概念!!
这也是Moq
框架被众多开发人员诟病的一点。
这时有读者会问了,讲这么多概念,头都晕了,但是对我写单元测试没有一点用呀。
不是的,分清这个概念最重要的一点就是不要让你写出下面的代码:
这个是同时Stub
和Mock
了同一个方法。非常正确,但是无用。
关键是这种写法出现在了我们团队真实的单元测试代码中。。。
名不正,则言不顺;言不顺,则事不成
理论不清晰的行动,总是不能在长期的战斗中获胜
在单元测试框架中,(尤其是Moq
这种概念不清楚的),时刻清除你的伪对象在做什么,是非常重要的一点
本文会经常更新,请阅读原文: https://xinyuehtx.github.io/post/Moq%E5%9F%BA%E7%A1%80-%E4%BA%8C.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名黄腾霄(包含链接: https://xinyuehtx.github.io ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 。