testable-mock/docs/zh-cn/doc/design-and-mechanism.md
2021-04-06 00:03:38 +08:00

30 lines
2.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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参与讨论、贡献😃