diff --git a/docs/zh-cn/doc/design-and-mechanism.md b/docs/zh-cn/doc/design-and-mechanism.md new file mode 100644 index 0000000..77ef82b --- /dev/null +++ b/docs/zh-cn/doc/design-and-mechanism.md @@ -0,0 +1,29 @@ +TestableMock的设计和原理 +--- + +这篇文档主要介绍`TestableMock`中Mock功能的设计思想和实现原理。 + +与常见的Mock工具在每个测试用例里写Mock定义不同,`TestableMock`让每个业务类直接提供自己的Mock方法集合,描述自身在测试时需要被Mock的调用以及相应替代逻辑(即每个业务类有自己的独立Test类和独立Mock类)。采用约定优于配置,降低Mock学习理解成本、减少冗余信息。 + +这种设计基于两项基本假设: + +1. 同一个测试类里,一个测试用例里需要Mock掉的方法,在其他测试用例里通常也都需要Mock。因为这些被Mock的方法往往访问了不便于测试的外部依赖。 +2. 需要Mock的调用都来自被测类的代码。(此假设是符合单元测试初衷的,即单元测试只应该关注当前单元的内部行为,单元外的逻辑应该被替换为Mock) + +据此通过约定来简化符合该假设的单元测试场景,通过配置来支持其余复杂的使用场景。 + +`TestableMock`的原理可以用一句话概括:利用JavaAgent动态修改字节码,把被测的业务类中与所有与Mock方法定义匹配的调用在单元测试运行时替换成对Mock方法的调用。 + +最终达到的效果则是,不论代码用什么服务框架、什么对象容器,不论要Mock的目标对象是注入的、new出来的、全局的还是局部的,不论要Mock的目标方法是私有的、外部的、静态的、继承来的或者重载过的,全部无差别通吃,让单元测试回归简单。 + +> 划重点:Mock的目标是**被测类**中的**方法调用**。测试用例里的代码不会被Mock,方法的定义本身没有变化,只是发起调用的代码被替换了。 + +具体来说,在单元测试启动时,`TestableMock`会对加载到内存中的类进行预处理,同时分别建立“被测类”、“测试类”、“Mock容器类”之间的关联关系(可以是一对一,也可以是多对一)。这个关联一方面是为了在测试用例执行时能够正确匹配Mock调用并进行替换,另一方面则是为了能控制Mock方法的生效范围。 + +对于被测类,将匹配到的调用换成对Mock容器方法的调用。 + +对于测试类,在每个测试用例开头插入Mock上下文初始化代码。 + +对于Mock容器类,增加`testableIns()`方法变成单例类,在每个Mock方法开头插入记录调用的代码。 + +以上是整个Mock的核心逻辑,更多实现细节,请参考源码。若有任何问题、建议、改进提议,都欢迎通过Github Issue和Pull Request参与讨论、贡献😃 diff --git a/docs/zh-cn/sidebar.md b/docs/zh-cn/sidebar.md index 1121c5e..9da8115 100644 --- a/docs/zh-cn/sidebar.md +++ b/docs/zh-cn/sidebar.md @@ -19,6 +19,7 @@ - [升级到0.5版本](zh-cn/doc/upgrade-to-v05.md) - [全局运行参数](zh-cn/doc/javaagent-args.md) - [注解参数清单](zh-cn/doc/annotations.md) + - [Mock的设计和原理](zh-cn/doc/design-and-mechanism.md) - [主流Mock工具对比](zh-cn/doc/comparation.md) - [版本更新](zh-cn/doc/release-note.md) - [关于我们](zh-cn/doc/about-us.md)