testable-mock/docs/zh-cn/doc/use-mock.md

121 lines
6.9 KiB
Markdown
Raw Normal View History

2020-11-22 22:12:51 +08:00
快速Mock被测类的任意方法调用
2020-10-23 23:22:53 +08:00
---
2020-12-12 00:05:53 +08:00
相比以往Mock工具以类为粒度的Mock方式`TestableMock`允许用户直接定义需要Mock的单个方法并遵循约定优于配置的原则按照规则自动在测试运行时替换被测方法中的指定方法调用。
2020-11-28 17:03:39 +08:00
2020-12-19 00:52:32 +08:00
> 归纳起来就两条:
> - Mock非构造方法拷贝原方法定义到测试类增加一个与调用者类型相同的参数加`@MockMethod`注解
> - Mock构造方法拷贝原方法定义到测试类返回值换成构造的类型方法名随意加`@MockContructor`注解
> **Mock约定**
> - 测试类与被测类的包路径应相同,且名称为`被测类名+Test`。通常采用`Maven`或`Gradle`构建的Java项目均符合这种惯例。此约定在未来的`TestableMock`版本中可能会被放宽或去除(请关注[Issue-12](https://github.com/alibaba/testable-mock/issues/12))。
> - 包含`@MockMethod`或`@MockContructor`注解的方法会在运行期被自动修改为`static`方法,请勿在这些方法中访问任何非`static`成员。为保险起见,建议将这些方法直接定义为`static`在未来版本中会对非静态定义的Mock方法增加编译期警告。
2020-12-23 10:16:37 +08:00
2020-11-28 17:03:39 +08:00
具体的Mock方法定义约定如下
2020-11-22 22:12:51 +08:00
#### 1. 覆写任意类的方法调用
2020-10-23 23:22:53 +08:00
在测试类里定义一个有`@MockMethod`注解的普通方法,使它与需覆写的方法名称、参数、返回值类型完全一致,然后在其参数列表首位再增加一个类型为该方法原本所属对象类型的参数。
2020-10-23 23:22:53 +08:00
此时被测类中所有对该需覆写方法的调用将在单元测试运行时将自动被替换为对上述自定义Mock方法的调用。
2020-10-23 23:22:53 +08:00
**注意**:当遇到待覆写方法有重名时,可以将需覆写的方法名写到`@MockMethod`注解的`targetMethod`参数里这样Mock方法自身就可以随意命名了。
2020-10-23 23:22:53 +08:00
例如,被测类中有一处`"anything".substring(1, 2)`调用,我们希望在运行测试的时候将它换成一个固定字符串,则只需在测试类定义如下方法:
```java
// 原方法签名为`String substring(int, int)`
// 调用此方法的对象`"anything"`类型为`String`
// 则Mock方法签名在其参数列表首位增加一个类型为`String`的参数(名字随意)
// 此参数可用于获得当时的实际调用者的值和上下文
@MockMethod
private static String substring(String self, int i, int j) {
return "sub_string";
}
```
2020-11-28 17:03:39 +08:00
下面这个例子展示了`targetMethod`参数的用法,其效果与上述示例相同:
```java
// 使用`targetMethod`指定需Mock的方法名
// 此方法本身现在可以随意命名,但方法参数依然需要遵循相同的匹配规则
@MockMethod(targetMethod = "substring")
private static String use_any_mock_method_name(String self, int i, int j) {
2020-11-28 17:03:39 +08:00
return "sub_string";
}
```
完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_mock_common_method()`测试用例。(由于Kotlin对String类型进行了魔改故Kotlin示例中将被测方法在`BlackBox`类里加了一层封装)
2020-10-23 23:22:53 +08:00
2020-11-22 22:12:51 +08:00
#### 2. 覆写被测类自身的成员方法
2020-10-23 23:22:53 +08:00
有时候在对某些方法进行测试时希望将被测类自身的另外一些成员方法Mock掉。
2020-10-23 23:22:53 +08:00
操作方法与前一种情况相同Mock方法的第一个参数类型需与被测类相同即可实现对被测类自身不论是公有或私有成员方法的覆写。
2020-10-23 23:22:53 +08:00
例如,被测类中有一个签名为`String innerFunc(String)`的私有方法(建议使用静态方法),我们希望在测试的时候将它替换掉,则只需在测试类定义如下方法:
```java
2020-11-22 00:10:12 +08:00
// 被测类型是`DemoMock`
// 因此在定义Mock方法时在目标方法参数首位加一个类型为`DemoMock`的参数(名字随意)
@MockMethod
private static String innerFunc(DemoMock self, String text) {
return "mock_" + text;
}
```
完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_mock_member_method()`测试用例。
2020-10-23 23:22:53 +08:00
2020-11-22 22:12:51 +08:00
#### 3. 覆写任意类的静态方法
2020-10-27 23:02:31 +08:00
2020-12-12 00:05:53 +08:00
对于静态方法的Mock与普通方法相同。但需要注意的是静态方法的Mock方法被调用时传入的第一个参数实际值始终是`null`。
2020-10-27 23:02:31 +08:00
例如,在被测类中调用了`BlackBox`类型中的静态方法`secretBox()`,改方法签名为`BlackBox secretBox()`则Mock方法如下
```java
// 目标静态方法定义在`BlackBox`类型中
// 在定义Mock方法时在目标方法参数首位加一个类型为`BlackBox`的参数(名字随意)
// 此参数仅用于标识目标类型,实际传入值将始终为`null`
@MockMethod
private static BlackBox secretBox(BlackBox ignore) {
return new BlackBox("not_secret_box");
}
```
完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_mock_static_method()`测试用例。
2020-10-27 23:02:31 +08:00
2020-11-22 22:12:51 +08:00
#### 4. 覆写任意类的new操作
2020-10-23 23:22:53 +08:00
在测试类里定义一个有`@MockContructor`注解的普通方法建议使用静态方法使该方法返回值类型为要被创建的对象类型且方法参数与要Mock的构造函数参数完全一致方法名称随意。
2020-10-23 23:22:53 +08:00
此时被测类中所有用`new`创建指定类的操作并使用了与Mock方法参数一致的构造函数将被替换为对该自定义方法的调用。
2020-10-23 23:22:53 +08:00
例如,在被测类中有一处`new BlackBox("something")`调用希望在测试时将它换掉通常是换成Mock对象或换成使用测试参数创建的临时对象则只需定义如下Mock方法
```java
// 要覆写的构造函数签名为`BlackBox(String)`
// 无需在Mock方法参数列表增加额外参数Mock方法的名称随意起
@MockContructor
private static BlackBox createBlackBox(String text) {
return new BlackBox("mock_" + text);
}
```
2020-12-12 08:42:08 +08:00
> 也可以依然使用`@MockMethod`注解,并配置`targetMethod`参数值为`"<init>"`,其余同上。效果与使用`@MockContructor`注解相同
完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_mock_new_object()`测试用例。
2020-10-23 23:22:53 +08:00
2020-11-22 22:12:51 +08:00
#### 5. 识别当前测试用例和调用来源
2020-10-23 23:22:53 +08:00
在Mock方法中可以通过`TestableTool.TEST_CASE`和`TestableTool.SOURCE_METHOD`来识别**当前运行的测试用例名称**和**进入该Mock方法前的被测类方法名称**,从而区分处理不同的调用场景。
2020-12-23 10:16:37 +08:00
> 这两个字段的实现机制基于调用堆栈分析尽管已经做了各种特殊情况的处理但在涉及多线程的复杂场景下依然存在误判的可能。若您发现了相关的可复现BUG请在Github提交Issue。
完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_get_source_method_name()`和`should_able_to_get_test_case_name()`测试用例。
2020-11-10 06:54:01 +08:00
2020-11-22 22:12:51 +08:00
#### 6. 验证Mock方法被调用的顺序和参数
2020-11-10 06:54:01 +08:00
在测试用例中可用通过`TestableTool.verify()`方法,配合`with()`、`withInOrder()`、`without()`、`withTimes()`等方法实现对Mock调用情况的验证。
2020-11-22 22:12:51 +08:00
详见[校验Mock调用](zh-cn/doc/matcher.md)文档。