mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-01-11 04:40:12 +08:00
30 lines
2.6 KiB
Markdown
30 lines
2.6 KiB
Markdown
TestableMock的设计和原理
|
||
---
|
||
|
||
这篇文档主要介绍`TestableMock`中Mock功能的设计思想和实现原理。
|
||
|
||
与常见的Mock工具在每个测试用例里写Mock定义不同,`TestableMock`让每个业务类直接提供自己的Mock方法集合,描述自身在测试时需要被Mock的调用以及相应替代逻辑(即每个业务类有自己的独立Test类和独立Mock类)。采用约定优于配置,降低Mock学习理解成本、减少冗余信息。
|
||
|
||
这种设计基于两项基本假设:
|
||
|
||
1. 同一个测试类里,一个测试用例里需要Mock掉的方法,在其他测试用例里通常也都需要Mock。因为这些被Mock的方法往往访问了不便于测试的外部依赖。
|
||
2. 需要Mock的调用都来自被测类的代码。(此假设是符合单元测试初衷的,即单元测试只应该关注当前单元的内部行为,单元外的逻辑应该被替换为Mock)
|
||
|
||
据此通过约定来简化符合该假设的单元测试场景,通过配置来支持其余复杂的使用场景。
|
||
|
||
`TestableMock`的原理可以用一句话概括:<u>利用JavaAgent动态修改字节码,把被测的业务类中与所有与Mock方法定义匹配的调用在单元测试运行时替换成对Mock方法的调用</u>。
|
||
|
||
最终达到的效果则是,不论代码用什么服务框架、什么对象容器,不论要Mock的目标对象是注入的、new出来的、全局的还是局部的,不论要Mock的目标方法是私有的、外部的、静态的、继承来的或者重载过的,全部无差别通吃,让单元测试回归简单。
|
||
|
||
> 划重点:Mock的目标是**被测类**中的**方法调用**。测试用例里的代码不会被Mock,方法的定义本身没有变化,只是发起调用的代码被替换了。
|
||
|
||
具体来说,在单元测试启动时,`TestableMock`会对加载到内存中的类进行预处理,同时分别建立“被测类”、“测试类”、“Mock容器类”之间的关联关系(可以是一对一,也可以是多对一)。这个关联一方面是为了在测试用例执行时能够正确匹配Mock调用并进行替换,另一方面则是为了能控制Mock方法的生效范围。
|
||
|
||
对于被测类,将匹配到的调用换成对Mock容器方法的调用。
|
||
|
||
对于测试类,在每个测试用例开头插入Mock上下文初始化代码。
|
||
|
||
对于Mock容器类,增加`testableIns()`方法变成单例类,在每个Mock方法开头插入记录调用的代码。
|
||
|
||
以上是整个Mock的核心逻辑,更多实现细节,请参考源码。若有任何问题、建议、改进提议,都欢迎通过Github Issue和Pull Request参与讨论、贡献😃
|