阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!
TestableMock 简介
TestableMock 开源地址:
TestableMock 在 2020 年 12 月开始开源,出自阿里云云效团队,主要想解决 Java 开发者在日常单元测试中经常遇到的痛点:
外部依赖Mock繁琐
私有方法难测试
无返回值方法难测试
复杂参数难构造
它所承载的职责是 “让Java没有难测的方法”,换种思路写Mock,让单元测试更简单,这也是 TestableMock 名字的来历。
无需初始化,不挑测试框架,甭管要换的是私有方法、静态方法、构造方法还是其他任何类的任何方法,也甭管要换的对象是怎么创建的。
写好 Mock 定义,加个 @MockMethod 注解,一切统统搞定。
主流Mock工具对比
在 TestableMock 开源之前,目前市面上主流的 Mock 工具主要有:
Mockito
Spock
PowerMock
JMockit
EasyMock
....
Mockito 应该是目前使用最多的 Mock 工具了,因为它使用足够简单,在 IntelliJ IDEA 和 Eclipse 开发工具上也都有专用的插件支持,但 Mock 功能相对来说还是较弱,不能覆盖所有应用场景。因为其使用的是动态代理技术,我们都知道,动态代理只能在方法前后环绕,有一定的局限性,所以 final 类型、静态方法、私有方法全都无法覆盖到。
上面所列的主流的 Mock 工具也只有 PowerMock 在功能上能够与 TestableMock 持平,但 PowerMock 使用较为复杂,而且由于使用的是自定义类加载器技术,所以也还会存在一定的问题。
下面来看下具体对比:
工具 | 原理 | 最小Mock单元 | 被Mock方法限制 | 难度 | IDE支持 |
---|---|---|---|---|---|
Mockito | 动态代理 | 类 | 不能Mock私有/静态和构造方法 | 较容易 | 很好 |
Spock | 动态代理 | 类 | 不能Mock私有/静态和构造方法 | 较复杂 | 一般 |
PowerMock | 自定义类加载器 | 类 | 任何方法皆可 | 较复杂 | 较好 |
JMockit | 运行时字节码修改 | 类 | 不能Mock构造方法 | 较复杂 | 一般 |
TestableMock | 运行时字节码修改 | 方法 | 任何方法皆可 | 很容易 | 一般 |
TestableMock 和 JMockit 底层一致,使用的是 "运行时字节码修改" 技术,在单元测试启动时就扫描测试类和被测类的字节码,完成 Mock 方法的替换。
现在综合看来,阿里开源的 TestableMock 是最牛逼的了,这是要干掉市面上所有 Mock 工具!另外,关注公众号Java技术栈,在后台回复:工具,可以获取我整理的 Java 开发工具系列干货,非常齐全。
上手 TestableMock
在项目中的 pom.xml 文件中增加 testable 相关依赖及单元测试相关依赖和插件,完整的配置如下:
<properties> <testable.version>0.4.9</testable.version> <junit.version>5.6.2</junit.version></properties><dependencies> <dependency> <groupId>com.alibaba.testable</groupId> <artifactId>testable-all</artifactId> <version>${testable.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency></dependencies><build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <argLine>-javaagent:${settings.localRepository}/com/alibaba/testable/testable-agent/${testable.version}/testable-agent-${testable.version}.jar</argLine> </configuration> </plugin> </plugins></build>
这里栈长以 Maven 为示例集成使用 TestableMock,Gradle 版本请参考官方文档。另外,关注公众号Java技术栈,在后台回复:Maven,可以获取我整理的 Maven 系列教程,非常齐全。
增加一个类,调用任意方法、成员方法、静态方法:
/** * @from 公众号:Java技术栈 * @author 栈长 */public class TestableMock { /** * 调用任意方法 */ public String commonMethod() { return " www ".trim() + "." + " javastack".substring(1) + "www.javastack.cn".startsWith(".com"); } /** * 调用成员、静态方法 */ public String memberMethod(String s) { return "{ \"result\": \"" + innerMethod(s) + staticMethod() + "\"}"; } private static String staticMethod() { return "WWW_JAVASTACK_CN"; } private String innerMethod(String website) { return "our website is: " + website; }}
增加单元测试类:
import com.alibaba.testable.core.annotation.MockMethod;import org.junit.jupiter.api.Test;import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;import static org.junit.jupiter.api.Assertions.assertEquals;/** * @author 栈长 * @from 公众号:Java技术栈 */class TestableMockTest { private TestableMock testableMock = new TestableMock(); /** * Mock 任意方法 * @return */ @MockMethod(targetClass = String.class) private String trim() { return "http://www"; } @MockMethod(targetClass = String.class, targetMethod = "substring") private String substr(int i) { return "javastack.cn_"; } @MockMethod(targetClass = String.class) private boolean startsWith(String website) { return false; } /** * Mock 成员方法 * @param text * @return */ @MockMethod(targetClass = TestableMock.class) private String innerMethod(String text) { return "mock_" + text; } /** * Mock 静态方法 * @return */ @MockMethod(targetClass = TestableMock.class) private String staticMethod() { return "_MOCK_JAVASTACK"; } @Test void commonMethodTest() { assertEquals("http://www.javastack.cn_false", testableMock.commonMethod()); verify("trim").withTimes(1); verify("substr").withTimes(1); verify("startsWith").withTimes(1); } @Test void memberMethodTest() { assertEquals("{ \"result\": \"mock_hello_MOCK_JAVASTACK\"}", testableMock.memberMethod("hello")); verify("innerMethod").withTimes(1); verify("staticMethod").withTimes(1); verify("innerMethod").with("hello"); verify("staticMethod").with(); } }
在以上单元测试类中,以 @MockMethod 注解标识的方法都是 Mock 方法,Mock 了任意方法、成员方法、静态方法。
使用确实很简单,非常灵活,功能也确实比动态代理那种要强大,一个 @MockMethod 注解走天下,可以扔掉其他的 Mock 工具了。
参考文档:
版权申明:本文系公众号 "Java技术栈" 原创,原创实属不易,转载、引用本文内容请注明出处,禁止抄袭、洗稿,请自重,尊重他人劳动成果和知识产权。