JAVA单元测试——JMockit教程

摘要

JMockit是一款Java类/接口/对象的Mock工具,目前广泛应用于Java应用程序的单元测试中。

首先,不会测试的不是好开发,不会Mock的不是好测试。因而,不会Mock的不是好开发。当然,Java Mock工具很多,比如easyMock,Mockito等等。为什么要选择JMockit呢?其实也没有啥特别原因,Mock工具的原理都差不多,就看Mock工具封装的API是否易用了。JMockit的API易用,丰富! 写出来的Mock程序代码完全面向对象。

一、JMockit的示例

java 复制代码
  <dependency>
    <groupId>org.jmockit</groupId>
    <artifactId>jmockit</artifactId>
    <version>1.36</version>
    <scope>test</scope>
  </dependency>
java 复制代码
//一个简单的类,能用不同语言打招呼
public class HelloJMockit {
    // 向JMockit打招呼
    public String sayHello() {
        Locale locale = Locale.getDefault();
        if (locale.equals(Locale.CHINA)) {
            // 在中国,就说中文
            return "你好,JMockit!";
        } else {
            // 在其它国家,就说英文
            return "Hello,JMockit!";
        }
    }
}
//HelloJMockit类的测试类
public class HelloJMockitTest { 
 
    /**
     * 测试场景:当前是在中国
     */
    @Test
    public void testSayHelloAtChina() {
        // 假设当前位置是在中国
        new Expectations(Locale.class) {
            {
                Locale.getDefault();
                result = Locale.CHINA;
            }
        };
        // 断言说中文
        Assert.assertTrue("你好,JMockit!".equals((new HelloJMockit()).sayHello()));
    }
 
    /**
     * 测试场景:当前是在美国
     */
    @Test
    public void testSayHelloAtUS() {
        // 假设当前位置是在美国
        new Expectations(Locale.class) {
            {
                Locale.getDefault();
                result = Locale.US;
            }
        };
        // 断言说英文
        Assert.assertTrue("Hello,JMockit!".equals((new HelloJMockit()).sayHello()));
    }
}

在上面的例子中,为了对依赖(当前的位置)进行Mock,用简单的3行代码即可搞定。把测试代码的依赖抽象成期待(Expectations),并把期待类Expectations作为本测试程序的内部类,可以任意访问本测试程序类的所有成员,为编写Mock程序提供极大便利。API面向对象特性封装良好。

此外,JMockit还提供了注解,支持泛型的Mock API用于对类/对象的属性,方法(支持static,private,final,native),构造函数,初始代码块(含静态初始代码块)灵活Mock。可以说,JMockit是一款功能强大,API易用,不可或缺的Java Mock工具。

二、JMockit的程序结构

java 复制代码
//JMockit的程序结构
public class ProgramConstructureTest {
 
    // 这是一个测试属性
    @Mocked
    HelloJMockit helloJMockit;
 
    @Test
    public void test1() {
        // 录制(Record)
        new Expectations() {
            {
                helloJMockit.sayHello();
                // 期待上述调用的返回是"hello,david",而不是返回"hello,JMockit"
                result = "hello,david";
            }
        };
        // 重放(Replay)
        String msg = helloJMockit.sayHello();
        Assert.assertTrue(msg.equals("hello,david"));
        // 验证(Verification)
        new Verifications() {
            {
                helloJMockit.sayHello();
 
                times = 1;
            }
        };
    }
 
    @Test
    public void test2(@Mocked HelloJMockit helloJMockit /* 这是一个测试参数 */) {
        // 录制(Record)
        new Expectations() {
            {
                helloJMockit.sayHello();
                // 期待上述调用的返回是"hello,david",而不是返回"hello,JMockit"
                result = "hello,david";
            }
        };
        // 重放(Replay)
        String msg = helloJMockit.sayHello();
        Assert.assertTrue(msg.equals("hello,david"));
        // 验证(Verification)
        new Verifications() {
            {
                helloJMockit.sayHello();
                // 验证helloJMockit.sayHello()这个方法调用了1次
                times = 1;
            }
        };
    }
}

通过上述例子可以看出,JMockit的程序结构包含了测试属性或测试参数,测试方法,测试方法体中又包含录制代码块,重放测试逻辑,验证代码块。

测试属性:

即测试类的一个属性。它作用于测试类的所有测试方法。在JMockit中,我们可以用JMockit的注解API来修饰它。这些API有**@Mocked, @Tested, @Injectable,@Capturing**。在上述例子中,我们用@Mocked修饰了测试属性HelloJMockit helloJMockit,表示helloJMockit这个测试属性,它的实例化,属性赋值,方法调用的返回值全部由JMockit来接管,接管后,helloJMockit的行为与HelloJMockit类定义的不一样了,而是由录制脚本来定义了。那@Mocked修饰后的helloJMockit,JMockit对它到底做了什么, 会在后续的章节中详细讲述。

测试参数:

即测试方法的参数。它仅作用于当前测试方法。给测试方法加参数,原本在JUnit中是不允许的,但是如果参数加了JMockit的注解API(@Mocked, @Tested, @Injectable,@Capturing),则是允许的。测试参数与测试属性的不同,主要是作用域的不同。

Record-Replay-Verification

Record-Replay-Verification 是JMockit测试程序的主要结构。

  1. Record: 即先录制某类/对象的某个方法调用,在当输入什么时,返回什么。
  2. Replay: 即重放测试逻辑。
  3. Verification: 重放后的验证。比如验证某个方法有没有被调用,调用多少次。

其实,Record-Replay-Verification与JUnit程序的AAA(Arrange-Action-Assert)结构是一样的。

Record对应Arrange,先准备一些测试数据,测试依赖。Replay对应Action,即执行测试逻辑。Verification对应Assert,即做测试验证。

2.1 @Mocked原理

当@Mocked修饰一个类时

java 复制代码
 //@Mocked注解用途
public class MockedClassTest {
    // 加上了JMockit的API @Mocked, JMockit会帮我们实例化这个对象,不用担心它为null
    @Mocked
    Locale locale;
 
    // 当@Mocked作用于class
    @Test
    public void testMockedClass() {
        // 静态方法不起作用了,返回了null
        Assert.assertTrue(Locale.getDefault() == null);
        // 非静态方法(返回类型为String)也不起作用了,返回了null
        Assert.assertTrue(locale.getCountry() == null);
        // 自已new一个,也同样如此,方法都被mock了
        Locale chinaLocale = new Locale("zh", "CN");
        Assert.assertTrue(chinaLocale.getCountry() == null);
    }
  
}

当@Mocked修饰一个接口/抽象类时

java 复制代码
//@Mocked注解用途
public class MockedInterfaceTest {
 
    // 加上了JMockit的API @Mocked, JMockit会帮我们实例化这个对象,尽管这个对象的类型是一个接口,不用担心它为null
    @Mocked
    HttpSession session;
 
    // 当@Mocked作用于interface
    @Test
    public void testMockedInterface() {
        // (返回类型为String)也不起作用了,返回了null
        Assert.assertTrue(session.getId() == null);
        // (返回类型为原始类型)也不起作用了,返回了0
        Assert.assertTrue(session.getCreationTime() == 0L);
        // (返回类型为原非始类型,非String,返回的对象不为空,这个对象也是JMockit帮你实例化的,同样这个实例化的对象也是一个Mocked对象)
        Assert.assertTrue(session.getServletContext() != null);
        // Mocked对象返回的Mocked对象,(返回类型为String)的方法也不起作用了,返回了null
        Assert.assertTrue(session.getServletContext().getContextPath() == null);
    }
}

通过上述例子,可以看出:@Mocked修饰的类/接口,是告诉JMockit,帮我生成一个Mocked对象,这个对象方法(包含静态方法)返回默认值。即如果返回类型为原始类型(short,int,float,double,long)就返回0,如果返回类型为String就返回null,如果返回类型是其它引用类型,则返回这个引用类型的Mocked对象(这一点,是个递归的定义,需要好好理解一下)。

什么测试场景,我们要使用@Mocked:当我们的测试程序依赖某个接口时,用@Mocked非常适合了。只需要@Mocked一个注解,JMockit就能帮我们生成这个接口的实例。 比如在分布式系统中,我们的测试程序依赖某个接口的实例是在远程服务器端时,我们在本地构建是非常困难的,此时就交给@Mocked,就太轻松啦!

博文参考

JMockit技术的分享文章

相关推荐
哥坐11路3 分钟前
网络IP跳动问题解决详
开发语言·php
奔跑吧邓邓子26 分钟前
【Python爬虫(27)】探索数据可视化的魔法世界
开发语言·爬虫·python·数据可视化
qq_124987075327 分钟前
Java+SpringBoot+Vue+数据可视化的美食餐饮连锁店管理系统
java·spring boot·毕业设计·美食
code bean37 分钟前
【C# 数据结构】队列 FIFO
开发语言·数据结构·c#
m0_748248231 小时前
Spring Framework 中文官方文档
java·后端·spring
Vacant Seat1 小时前
矩阵-矩阵置零
java·矩阵·二维数组
先睡1 小时前
Spring MVC的基本概念
java·spring·mvc
m0_748240541 小时前
Springboot项目:使用MockMvc测试get和post接口(含单个和多个请求参数场景)
java·spring boot·后端
CoderCodingNo1 小时前
【GESP】C++二级真题 luogu-b3865, [GESP202309 二级] 小杨的 X 字矩阵
java·c++
暗诺星刻1 小时前
Java 数学函数库
java·数学·函数·计算器·计算