Java单元测试Mock神器

607 阅读2分钟

TestableMock 简介

官方介绍"一款特立独行的轻量 Mock 工具,以简化 Java 单元测试为目标的综合辅助工具集"

Maven 项目中使用

dependencies列表添加 TestableMock 依赖

<dependency>
    <groupId>com.alibaba.testable</groupId>
    <artifactId>testable-all</artifactId>
    <version>0.7.9</version>
    <scope>test</scope>
</dependency>

最后在build区域的plugins列表里添加maven-surefire-plugin插件

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <argLine>-javaagent:${settings.localRepository}/com/alibaba/testable/testable-agent/0.7.9/testable-agent-0.7.9.jar</argLine>
    </configuration>
</plugin>

TestableMock 可以 Mock 什么

快速 Mock 任意调用

官方介绍"使被测类的任意方法调用快速替换为 Mock 方法,实现"指哪换哪",解决传统 Mock 工具使用繁琐的问题"
使用场景
项目中有支付回调的场景,需要测试回调的方法逻辑是否正确。
痛点
方法中有调用其他 jar 包中的方法,而且该方法中还有很多逻辑,但这些逻辑并不是我们关注的
解决
待测方法

@Override
public String parseOrderNotifyResult(String xmlData) {
    try {
        WxPayOrderNotifyResult notifyResult = wxStudentMiniPayService.parseOrderNotifyResult(xmlData);
    ...
    ...

wxStudentMiniPayService.parseOrderNotifyResult(xmlData) 该方法为 maven 引入 jar 包中的方法,我们的目的就是 Mock 该方法,从而只关注 待测方法的逻辑
测试 case,直接看代码

@SpringBootTest
public class PayServiceImplTest extends AbstractTestNGSpringContextTests {

    @Resource
    private PayServiceImpl payService;

    public static class Mock{
        @MockInvoke(targetClass = WxPayService.class)
        public WxPayOrderNotifyResult parseOrderNotifyResult(String xmlData) {
            WxPayOrderNotifyResult wxPayOrderNotifyResult = new WxPayOrderNotifyResult();
            wxPayOrderNotifyResult.setTransactionId("11111");
            wxPayOrderNotifyResult.setResultCode("SUCCESS");
            wxPayOrderNotifyResult.setOutTradeNo("74afe6ab214248b49898a832da79b742");
            wxPayOrderNotifyResult.setDeviceInfo("KLASS");
            return wxPayOrderNotifyResult;
        }
    }

    @Test(description = "支付回调")
    public void testParseOrderNotifyResult(){
        String xmlData = "";
        String res = payService.parseOrderNotifyResult(xmlData);
        ...
    }

}

此时,执行测试 case,待测方法中WxPayOrderNotifyResult notifyResult = wxStudentMiniPayService.parseOrderNotifyResult(xmlData) notifyResult 返回的结果就是 Mock 容器中对应的数据。

上面代码,就是在测试类中添加了一个名称为 Mock 的静态内部类,拷贝原方法定义到 Mock 容器类,加@MockInvoke 注解。这也是官方推荐的最简单的做法

访问被测类私有成员

TestableMock 提供了 PrivateAccessor 工具类

访问私有方法

PrivateAccessor.invoke(任意对象, "私有方法名", 调用参数...) ➜ 调用任意类的私有方法
使用场景
通过公有方法间接测私有方法比较繁琐,公有方法有复杂的入参,核心业务逻辑在私有方法中,我们更加关注私有方法的业务逻辑。
痛点
未使用 TestableMock 时,我是修改了方法的可见性,private 变为 public,这种方法比较繁琐
解决
待测方法

private Student register(Map<String, String> paramMap) {
    ...
}

如何借助 PrivateAccessor 测试该私有方法呢?

@Test(description = "团购入口注册")
public void registerTest() {
    Map<String, String> paramMap = new HashMap<String, String>(){
        {
            put("areaCode", "86");
            put("mobile", "19900000007");
        }
    };
    Student student = PrivateAccessor.invoke(groupService, "register", paramMap);
    ...
}

除了访问私有方法,还读取私有字段、修改私有字段、读取静态私有字段、修改静态私有字段、调用静态私有方法、调用私有构造方法,遇到真实使用场景再进行补充

持续更新中...

参考资料
alibaba.github.io/testable-mo…