JMockit:使用 Expectations 录制函数行为


#Java Jmockit#


介绍

使用 Expectations 录制函数行为,然后通过实际的单测逻辑重放录制的函数行为。

示例

被测试类

package demo;

public class Calculator {

    // 非静态函数
    public int add(int a, int b) {
        return a+b;
    }

    // 静态函数
    public static int staticAdd(int a, int b) {
        return a+b;
    }

    // 空函数
    public void noop() {
        System.out.println("没有被 mock");
    }

}

示例:指定参数,返回特定值

package demo;

import mockit.Expectations;
import org.junit.Test;

public class CalculatorTest {

    @Test
    public void test_add_01() {
        Calculator calculator = new Calculator();
        new Expectations(Calculator.class) {{
            // 当参数分别是 1、2 时,返回1
            calculator.add(1, 2); result = 1;
        }};

        System.out.println(calculator.add(1, 2));
        System.out.println(calculator.add(2, 2));
    }

}

执行结果:

1
4

另外一种写法是用 withEqual 匹配参数精确值:

package demo;

import mockit.Expectations;
import org.junit.Test;

public class CalculatorTest {

    @Test
    public void test_add_01() {
        Calculator calculator = new Calculator();
        new Expectations(Calculator.class) {{
            // 当参数分别是 1、2 时,返回1
            calculator.add(withEqual(1), withEqual(2)); result = 1;
        }};

        System.out.println(calculator.add(1, 2));
        System.out.println(calculator.add(2, 2));
    }

}

类似 withEqual 的方法还有很多,可以跳转到源代码查看。

示例:不同单测之间的 mock 是隔离的

package demo;

import mockit.Expectations;
import org.junit.Test;

public class CalculatorTest {

    @Test
    public void test_add_01() {
        System.out.println("--- test_add_01 ---");
        Calculator calculator = new Calculator();
        new Expectations(Calculator.class) {{
            // 当参数分别是 1、2 时,返回1
            calculator.add(1, 2); result = 1;
        }};

        System.out.println(calculator.add(1, 2));
        System.out.println(calculator.add(2, 2));
    }


    @Test
    public void test_add_02() {
        System.out.println("--- test_add_02 ---");
        System.out.println(Calculator.staticAdd(1, 2));  // 不会被 test_add_01 的mock影响到
    }

}

执行结果:

--- test_add_01 ---
1
4
--- test_add_02 ---
3

示例:任意参数,返回特定值

package demo;

import mockit.Expectations;
import org.junit.Test;

public class CalculatorTest {

    @Test
    public void test_add_01() {
        Calculator calculator = new Calculator();
        new Expectations(Calculator.class) {{
            calculator.add(anyInt, anyInt); result = 1;
        }};

        System.out.println(calculator.add(1, 2));
        System.out.println(calculator.add(2, 2));
    }

}

执行结果:

1
1

注意:

anyInt 的定义是 @Nonnull protected final Integer anyInt = 0; ,但是上面的 Expectations 如果改成:

new Expectations(Calculator.class) {{
    calculator.add(0, 0); result = 1;
}};

执行结果会变成:

3
4

Missing 1 invocation to:
demo.Calculator#add(0, 0)
   on mock instance: demo.Calculator@532f2596

这意味着 anyInt 与 0 不等价。为什么?以后探索。

另外,默认情况下 Expectations 录制的函数,必须被执行一次,否则会报错 Missing 1 invocation to: 之类的错误。

示例:录制后,不重放,也不报错

指定 minTimes 为 0 即可。相关的变量还有 timesmaxTimes

package demo;

import mockit.Expectations;
import org.junit.Test;

public class CalculatorTest {

    // 会报错
    @Test
    public void test_add_01() {
        Calculator calculator = new Calculator();
        new Expectations(Calculator.class) {{
            calculator.add(1, 2); result = 1;
        }};
    }

}

执行时会报错:

Missing 1 invocation to:
demo.Calculator#add(1, 2)
......

因为 minTimes 默认为 1。

将 minTimes 设置为 0,则不会报错:

package demo;

import mockit.Expectations;
import org.junit.Test;

public class CalculatorTest {

    // 不会报错
    @Test
    public void test_add_01() {
        Calculator calculator = new Calculator();
        new Expectations(Calculator.class) {{
            calculator.add(1, 2); result = 1; minTimes = 0;
        }};
    }

}

执行后不会报错。

示例:让函数什么都不做

package demo;

import mockit.Expectations;
import org.junit.Test;

public class CalculatorTest {

    @Test
    public void test_add_01() {
        Calculator calculator = new Calculator();
        new Expectations(Calculator.class) {{
            calculator.noop();
        }};

        calculator.noop();  // 执行时不会输出内容
    }

}

示例:让函数抛异常

让 result 的值是一个异常类即可。

package demo;

import mockit.Expectations;
import org.junit.Test;

public class CalculatorTest {

    @Test
    public void test_add_01() {
        Calculator calculator = new Calculator();
        new Expectations(Calculator.class) {{
            calculator.noop(); result = new RuntimeException("异常");
        }};

        calculator.noop();
    }

}

执行结果:

异常
java.lang.RuntimeException: 异常
	at demo.CalculatorTest.test_add_01(CalculatorTest.java:16)
	at java.base/java.lang.Thread.run(Thread.java:829)

示例:同一个类,不同对象,分别录制

new Expectations(Calculator.class) {{
    // 当参数分别是 1、2 时,返回1
    calculator.add(1, 2); result = 1;
}};

上面这种方式是针对 Calculator 的所有对象。

如果要不同对象,分别录制,则:

package demo;

import mockit.Expectations;
import org.junit.Test;

public class CalculatorTest {

    @Test
    public void test_add_01() {
        Calculator calculator01 = new Calculator();
        Calculator calculator02 = new Calculator();

        new Expectations(calculator01, calculator02) {{
            calculator01.add(1, 2); result = 100;
            calculator02.add(1, 2); result = 200;
        }};

        System.out.println(calculator01.add(1, 2));
        System.out.println(calculator02.add(1, 2));
    }

}

执行结果:

100
200

示例:录制静态函数行为

package demo;

import mockit.Expectations;
import org.junit.Test;

public class CalculatorTest {

    @Test
    public void test_add_01() {

        new Expectations(Calculator.class) {{
            Calculator.staticAdd(1, 2); result = 1;
        }};

        System.out.println(Calculator.staticAdd(1, 2));
    }

}

执行结果:

1

示例:同一函数每次返回的结果不一样

package demo;

import mockit.Expectations;
import org.junit.Test;

public class CalculatorTest {

    @Test
    public void test_add_01() {
        new Expectations(Calculator.class) {{
            Calculator.staticAdd(1, 2);
            result = 1;   // 第1次调用时返回1
            result = 2;   // 第2次,以及之后的调用返回1
        }};

        System.out.println(Calculator.staticAdd(1, 2));
        System.out.println(Calculator.staticAdd(1, 2));
        System.out.println(Calculator.staticAdd(1, 2));
    }

}

执行结果:

1
2
2

另外一种方式是,返回数组:

package demo;

import mockit.Expectations;
import org.junit.Test;

public class CalculatorTest {

    @Test
    public void test_add_01() {
        new Expectations(Calculator.class) {{
            Calculator.staticAdd(1, 2);
            result = new int[] {1, 2};
        }};

        System.out.println(Calculator.staticAdd(1, 2));
        System.out.println(Calculator.staticAdd(1, 2));
        System.out.println(Calculator.staticAdd(1, 2));
    }

}

( 本文完 )