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

4.8 KiB
Raw Blame History

自助问题排查

相比Mockito等由开发者手工放置Mock类的做法TestableMock使用方法名和参数类型匹配自动寻找需Mock的调用。这种机制在带来方便的同时也有可能发生预料之外的Mock替换。

若要排查Mock相关的问题只需在测试类上添加@MockWith注解,并配置参数diagnose值为MockDiagnose.ENABLE在运行测试时就会打印出详细的Mock方法替换过程。

@MockWith(diagnose = MockDiagnose.ENABLE)
class DemoTest {
    ...
}

输出日志示例如下:

[DIAGNOSE] Handling test class com/alibaba/testable/demo/DemoMockTest
[DIAGNOSE]   Found 8 mock methods
[DIAGNOSE] Handling source class com/alibaba/testable/demo/DemoMock
[DIAGNOSE]   Handling method <init>
[DIAGNOSE]   Handling method newFunc
[DIAGNOSE]     Line 19, mock method "createBlackBox" used
[DIAGNOSE]   Handling method outerFunc
[DIAGNOSE]     Line 27, mock method "innerFunc" used
[DIAGNOSE]     Line 27, mock method "staticFunc" used
[DIAGNOSE]   Handling method commonFunc
[DIAGNOSE]     Line 34, mock method "trim" used
[DIAGNOSE]     Line 34, mock method "sub" used
[DIAGNOSE]     Line 34, mock method "startsWith" used
... ...

该日志展示了被测类中所有发生了Mock替换的调用和相应代码行号。

简单排查方法:

  • 若没有任何输出,请检查pom.xmlbuild.gradle配置是否正确引入了TestableMock依赖
  • 若只输出了Handling test class,请检查被测类与测试类是否包路径相同,且名称为"被测类+Test"0.4.x版本要求)
  • 若输出了Handling source class以及Handling method xxx但预期的代码行位置没有发生Mock替换请检查Mock方法定义是否未与目标方法匹配

对于预期Mock未生效的情况如需进一步排查可将日志级别提升到MockDiagnose.VERBOSE

@MockWith(diagnose = MockDiagnose.VERBOSE)
class DemoTest {
    ...
}

再次执行单元测试将会打印出所有Mock方法的运行期签名以及被测类中扫描到所有调用的运行期签名

[DIAGNOSE] Handling test class com/alibaba/testable/demo/DemoMockTest
[VERBOSE]    Mock constructor "createBlackBox" as "(Ljava/lang/String;)V" for "com/alibaba/testable/demo/model/BlackBox"
[VERBOSE]    Mock method "innerFunc" as "(Ljava/lang/String;)Ljava/lang/String;"
[VERBOSE]    Mock method "staticFunc" as "()Ljava/lang/String;"
[VERBOSE]    Mock method "trim" as "()Ljava/lang/String;"
[VERBOSE]    Mock method "sub" as "(II)Ljava/lang/String;"
[VERBOSE]    Mock method "startsWith" as "(Ljava/lang/String;)Z"
[VERBOSE]    Mock method "secretBox" as "()Lcom/alibaba/testable/demo/model/BlackBox;"
[VERBOSE]    Mock method "callFromDifferentMethod" as "()Ljava/lang/String;"
[DIAGNOSE]   Found 8 mock methods
[DIAGNOSE] Handling source class com/alibaba/testable/demo/DemoMock
[DIAGNOSE]   Handling method <init>
[VERBOSE]      Line 13, constructing "java/lang/Object" as "()V"
[DIAGNOSE]   Handling method newFunc
[VERBOSE]      Line 19, constructing "com/alibaba/testable/demo/model/BlackBox" as "(Ljava/lang/String;)V"
[DIAGNOSE]     Line 19, mock method "createBlackBox" used
[VERBOSE]      Line 19, invoking "createBlackBox" as "(Ljava/lang/String;)Lcom/alibaba/testable/demo/model/BlackBox;"
[VERBOSE]      Line 20, invoking "get" as "()Ljava/lang/String;"
[DIAGNOSE]   Handling method outerFunc
[VERBOSE]      Line 27, constructing "java/lang/StringBuilder" as "()V"
[VERBOSE]      Line 27, invoking "append" as "(Ljava/lang/String;)Ljava/lang/StringBuilder;"
[VERBOSE]      Line 27, invoking "innerFunc" as "(Ljava/lang/String;)Ljava/lang/String;"
[DIAGNOSE]     Line 27, mock method "innerFunc" used
[VERBOSE]      Line 27, invoking "innerFunc" as "(Ljava/lang/String;)Ljava/lang/String;"
[VERBOSE]      Line 27, invoking "append" as "(Ljava/lang/String;)Ljava/lang/StringBuilder;"
[VERBOSE]      Line 27, invoking "staticFunc" as "()Ljava/lang/String;"
[DIAGNOSE]     Line 27, mock method "staticFunc" used
[VERBOSE]      Line 27, invoking "append" as "(Ljava/lang/String;)Ljava/lang/StringBuilder;"
[VERBOSE]      Line 27, invoking "append" as "(Ljava/lang/String;)Ljava/lang/StringBuilder;"
[VERBOSE]      Line 27, invoking "toString" as "()Ljava/lang/String;"
... ...

输出日志结构参考如下:

  • Mock constructor "<Mock方法名>" as "<方法签名>" for "<类型>" 在测试类中扫描到的Mock构造方法及其运行时签名
  • Mock method "<Mock方法名>" as "<方法签名>" 在测试类中扫描到的普通Mock方法及其运行时签名打印时暂未自动排除用于指定Mock目标类的首位参数
  • Line XX, constructing "<类型>" as "<方法签名>" 在被测类中扫描掉的构造方法调用及其运行时签名
  • Line XX, invoking "<方法名>" as "<方法签名>" 在被测类中扫描到的成员方法调用及其运行时签名