8

阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!

 3 years ago
source link: https://www.cnblogs.com/javastack/p/14334476.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

最近栈长注意到阿里开源了自家的 Mock 工具:TestableMock,该工具号称最轻量、简单、舒适的 Mock 测试工具,功能十分强大,媲美 PowerMock,用法比 Mockito 还要简洁,还不挑框架,指哪换哪,一个 @MockMethod 注解打天下。。。

这么强大的么?栈长赶紧来体验一翻!

TestableMock 简介

TestableMock 开源地址:

https://github.com/alibaba/testable-mock

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技术栈" 原创,原创实属不易,转载、引用本文内容请注明出处,禁止抄袭、洗稿,请自重,尊重他人劳动成果和知识产权。

近期热文推荐:

1.Java 15 正式发布, 14 个新特性,刷新你的认知!!

2.终于靠开源项目弄到 IntelliJ IDEA 激活码了,真香!

3.我用 Java 8 写了一段逻辑,同事直呼看不懂,你试试看。。

4.吊打 Tomcat ,Undertow 性能很炸!!

5.《Java开发手册(嵩山版)》最新发布,速速下载!

觉得不错,别忘了随手点赞+转发哦!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK