代码地址:http://git.oschina.net/zhv/UnitTest
深入了解隔离框架
隔离框架分类
- 受限框架
- 有些东西不能伪造,不能伪造静态方法,非虚拟方法和非公共方法
- 受限框架是运行时生成和编译代码,和常规代码一样受到编译规则限制。
- 因此必须是公共可继承的,必须有公共构造函数,或者是一个接口。
- 受限框架有:Moq、NSubstitute、EasyMock、FakeItEasy等
- 不受限框架
- 所有.NET的不受限框架都是基于探查器的(profiler-based),使用了一套探查器API(profiling API)
- 对于CLR代码执行师发生的任何事情,这些API都提供了相应的事件。
- 这些.NET不受限框架可以对任何库,任何类,任何代码有效注入和修改其行为,即使他们不是你编译的也不成问题。
- 要开启探查器就要给进程设置探查器的环境变量。
- 不受限框架有:Typemock Isolator、JustMock、Moles(MS Fakes)
- 不受限框架优点
- 可以为以前无法测试的代码编写单元测试
- 可以伪造无法控制,而且极难协同测试的第三方系统
- 可以选择自己的设计层次,而不用被迫使用某些模式。
- 不受限框架缺点
- 可能会伪造不需要的东西。不能在高层次理解工作单元。
- 可能会伪造不属于你的API,导致一些测试无法维护。
不受限框架工作方式
- 像Typemock Isolator这样的工具会使用C++编写非托管代码,附加在CLR探查器API的COM接口上,注册到一个特殊的事件钩子回调函数(event-hook callback)
- 通过使用ICorProfilerCallback2 COM扣的两个方法(JimCompilationStarted和SetILFunctionBody),在中间语言代码编译成二进制代码前得到和修改这些即将执行的代码
- 这时就可以修改这些中间语言代码,使它包括你自己的定制中间语言代码。
优秀的隔离框架
对应的功能应该有:
- 递归伪对象
- 伪对象在函数返回其他对象时,返回对象自动成为伪对象,并一直递归下去
- 默认忽略参数
- 不用再所有方法中总是包含Arg.IsAny<Type>了。
- 泛伪造
- 伪对象的非严格行为
- 一个严格模拟对象在两种情况下会失败:1. 对它进行了一个非预期的方法调用; 2. 没有对它的预期方法进行调用(这个可以通过调用Received()决定)
- 其实如果不关心工作单元内部对象之间的内部协议,就不应该对它们之间的交互进行断言。所以这种情况下调用了非预期的方法而造成了测试失败。
- 给个不恰当的比喻:就好像我判断你有没有吃饭,而你在吃饭的时候喝了水,我的测试就失败了。失败的原因是你没告诉我吃饭的时候不能喝水。
- 非严格模拟对象
- 一个非严格模拟对象允许对它进行任何调用,即便这个调用不是预期的。
隔离框架设计反模式
概念混淆
- Mock这个词用的太多,比如Moq框架,里面不管生成桩还是模拟对象都用Mock这个词。
- 这种情况下就得把变量名命名为Mockxxx和Stubxxx来区分桩还是模拟对象。
录制和重放
- 降低了可读性,录制出来的可能是:准备-部分执行-准备或断言-部分执行-断言-准备-执行。
- 使用准备-执行-断言这个风格才是比较合适的。
粘性行为
复杂语法
- 有些就比较难记住基本操作
- 比较好的,比如是FakeItEasy中所有的可能操作都是A开头的
- 使用NSubstitute时则要用Substitute来创建伪对象,用真实对象的扩展方法验证或者修改行为,用Arg<T>使用参数匹配器。这个就有些复杂,不太友好了。
转载于:https://my.oschina.net/zhv/blog/908547