这一章是最后一讲,我们讲一下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要求必须同时伪造方法BC,否则会抛出异常。

同样,另一方面,如果后期方法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/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系