这一章是最后一讲,我们讲一下Moq
中值得注意的小技巧,以及对Moq
使用的评价
MockBehavior
:伪对象行为
在使用Moq
创建伪对象时,可以在构造函数里传入MockBehavior
MockBehavior
有了两种:
Loose
:默认行为,任何未显示伪造的方法和属性都会返回默认值,且不会抛出异常。Strict
: 任何调用都需要显式Setup
,并使用VerifyAll
验证。
什么意思呢?如下图所示,你要验证公共方法A
,A中做了T.B()
和T.C()
两件事。
public void A()
{
Name = T.B();
Age = T.C();
}
Loose
允许你测试A
时只伪造方法B
,并验证Name
状态,
Strict
要求必须同时伪造方法B
和C
,否则会抛出异常。
同样,另一方面,如果后期方法A
又调用了一个T.D()
,那么前者的测试会过,后者会失败,提醒用户修改测试。
至于选择,我个人是没有什么偏好,大家自己喜欢就好。
CallBase
:调用基类方法
如果你期望某些方法调用原类型虚方法的默认实现,可以使用
var mock = new Mock<IFoo> { CallBase = true };
这个在测试有一大堆虚方法的基类时十分有效,不用为了测一个方法,伪造过多其他方法。
SetupSequence
:伪造序列
如果你期望,一个方法每次调用返回值都不同,那么可以试试下面的写法。
var mock = new Mock<IFoo>();
mock.SetupSequence(f => f.GetCount())
.Returns(3) // will be returned on 1st invocation
.Returns(2) // will be returned on 2nd invocation
.Returns(1) // will be returned on 3rd invocation
.Returns(0) // will be returned on 4th invocation
.Throws(new InvalidOperationException()); // will be thrown on 5th invocation
值得注意的是如果你期望,该方法被调用4次,那么一定要在第5次(最后一句)
Throws(new InvalidOperationException())
中断测试,否则会返回Null
Protected()
:伪造Protected
成员
如果需要测试Protected
成员的行为,你可以使用下面的方式(不过到了这一步,可能已经意味着你的代码需要再审查一遍结构是否合理了)
//无参数
mock.Protected()
.Setup<int>("Execute")
.Returns(5);
//带参数
mock.Protected()
.Setup<string>("Execute",
ItExpr.IsAny<string>())
.Returns(true);
值得注意的是,因为Protected
成员“不可见”,因此只能使用字符串进行处理
Internal
程序集可见
有时候我们会需要测试一些Internal
的类和方法,此时我们不仅需要对测试项目可见,还要对测试框架的生成器可见。
因此需要在AssemblyInfo.cs
添加
[assembly:InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly:InternalsVisibleTo("YourTestProject")]
ok,以上是Moq
基础的全部知识性内容。
下面说说对Moq
的看法。
Moq
作为一个受限的单元测试框架,做到了免费,简单,易用。
应该说能够满足大部分的应用需求。
对于一个项目来说,如果Moq
能够满足使用需求,那这个项目一定是SOLID
的
当然,对于一些遗留代码来说,通常需要非受限框架对他进行支持(例如typemock isolator,MS Fakes)
不足之处是,Moq
的概念存在一些混淆,往往容易将初学者带偏,一些API设计也有待商榷。
但是,Moq
只是我们书写单元测试的工具,他真正的威力在于使用者。
兵无常势,水无常形。框架总会更新,创建测试的能力才是需要保留的。
本文会经常更新,请阅读原文: https://dotnet-campus.github.io//post/Moq%E5%9F%BA%E7%A1%80-%E5%85%AD.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 腾霄 (包含链接: https://dotnet-campus.github.io/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 。