Java uses JMockit to easily handle many test scenarios

JMockit is developed based on the java.lang.instrument package in JavaSE5. It uses the ASM library to dynamically modify the bytecode of java, so that the static language of java can dynamically set the private properties of the Mock object like a dynamic scripting language, simulating static, Private method behavior, etc. For mobile phone development, embedded development, etc., where the code is required to be as concise as possible, or if the tested code does not want to be modified, many test scenarios can be easily handled by using JMockit.

[20140104100723093][1]

Add JMockit-related dependencies in maven as follows:

        <dependency>  
            <groupId>com.googlecode.jmockit</groupId>  
            <artifactId>jmockit</artifactId>  
            <version>1.5</version>  
            <scope>test</scope>  
        </dependency>  
        <dependency>  
            <groupId>com.googlecode.jmockit</groupId>  
            <artifactId>jmockit-coverage</artifactId>  
            <version>0.999.24</version>  
            <scope>test</scope>  
        </dependency>

JMockit has two Mock methods: behavior-based Mock and state-based Mock:

Refer to the use of mock in unit testing and the JMockit API and tools in the practice of the mock artifact jmockit as follows:

[20140104102342843][2]

(1). Behavior-based Mock method:

Very similar to how EasyMock and PowerMock work, the basic steps are:

1. Record method expected behavior.

2. Real call.

3. Verify that the recorded behavior is invoked.

A simple example to introduce the basic process of JMockit:

The method to Mock test is as follows:

public class MyObject {
    public String hello(String name){
        return "Hello " + name;
    }
}

The unit tests written using JMockit are as follows:

@Mocked  //用@Mocked标注的对象,不需要赋值,jmockit自动mock  
MyObject obj;  

@Test  
public void testHello() {  
    new NonStrictExpectations() {//录制预期模拟行为  
        {  
            obj.hello("Zhangsan");  
            returns("Hello Zhangsan");  
            //也可以使用:result = "Hello Zhangsan";  
        }  
    };  
    assertEquals("Hello Zhangsan", obj.hello("Zhangsan"));//调用测试方法  
    new Verifications() {//验证预期Mock行为被调用  
        {  
            obj.hello("Hello Zhangsan");  
            times = 1;  
        }  
    };  
}  

JMockit can also be classified into non-local simulation and local simulation. The difference lies in whether the Expectations block has parameters. If there are parameters, it is a local simulation, otherwise it is a non-local simulation.

The Expectations block is generally defined by the Expectations class and the NonStrictExpectations class, similar to the Strict Mock and the general Mock in EasyMock and PowerMock.

Defined by the Expectations class, the mock object can only call methods in the order defined in the Expectations block at runtime, and cannot call more or less, so the Verifications block can be omitted;

However, those defined with the NonStrictExpectations class do not have these restrictions, so if verification is required, the Verifications block must be added.

The above example uses non-local mocking. Next, we use local mocking to rewrite the above test. The code is as follows:

@Test  
public void testHello() {  
    final MyObject obj = new MyObject();  
    new NonStrictExpectations(obj) {//录制预期模拟行为  
        {  
            obj.hello("Zhangsan");  
            returns("Hello Zhangsan");  
            //也可以使用:result = "Hello Zhangsan";  
        }  
    };  
    assertEquals("Hello Zhangsan", obj.hello("Zhangsan"));//调用测试方法  
    new Verifications() {//验证预期Mock行为被调用  
        {  
            obj.hello("Hello Zhangsan");  
            times = 1;  
        }  
    };  
}  

Mock static method:

@Test  
public void testMockStaticMethod() {  
    new NonStrictExpectations(ClassMocked.class) {  
        {  
            ClassMocked.getDouble(1);//也可以使用参数匹配:ClassMocked.getDouble(anyDouble);  
            result = 3;  
        }  
    };  

    assertEquals(3, ClassMocked.getDouble(1));  

    new Verifications() {  
        {  
            ClassMocked.getDouble(1);  
            times = 1;  
        }  
    };  
}  

Mock private method:

If the getTripleString(int) method in the ClassMocked class specifies calling a private multiply3(int) method, we can use the following method to Mock:

@Test  
public void testMockPrivateMethod() throws Exception {  
    final ClassMocked obj = new ClassMocked();  
    new NonStrictExpectations(obj) {  
        {  
            this.invoke(obj, "multiply3", 1);//如果私有方法是静态的,可以使用:this.invoke(null, "multiply3")  
            result = 4;  
        }  
    };  

    String actual = obj.getTripleString(1);  
    assertEquals("4", actual);  

    new Verifications() {  
        {  
            this.invoke(obj, "multiply3", 1);  
            times = 1;  
        }  
    };  
}  

Set the value of the private property of the Mock object: We know that the Mock objects of EasyMock and PowerMock are implemented through JDK/CGLIB dynamic proxy, which is essentially class inheritance or interface implementation, but in java object-oriented programming, the base class object Private properties cannot be inherited by subclasses, so if the method of the Mock object uses its own private properties, and these private properties do not provide object access methods, it is impossible to test using the traditional Mock method. JMockit provides The method to set the value of the private property of the Mocked object, the code is as follows: Tested code:

public class ClassMocked {  
    private String name = "name_init";  

    public String getName() {  
        return name;  
    }  

    private static String className="Class3Mocked_init";  

    public static String getClassName(){  
        return className;  
    }  
}  

Use JMockit to set private properties:

@Test  
public void testMockPrivateProperty() throws IOException {  
    final ClassMocked obj = new ClassMocked();  
    new NonStrictExpectations(obj) {  
        {  
            this.setField(obj, "name", "name has bean change!");  
        }  
    };  

    assertEquals("name has bean change!", obj.getName());  
}  

Use JMockit to set static private properties:

@Test  
public void testMockPrivateStaticProperty() throws IOException {  
    new NonStrictExpectations(Class3Mocked.class) {  
        {  
            this.setField(ClassMocked.class, "className", "className has bean change!");  
        }  
    };  

    assertEquals("className has bean change!", ClassMocked.getClassName());  
}  

(2). State-based Mock method:

The behavior-based Mock method on JMockit is basically similar to the traditional EasyMock and PowerMock processes, which is equivalent to treating the simulated method as a black box, while JMockit's state-based Mock can directly rewrite the internal logic of the simulated method, more like It is a white-box test in the true sense. The following is a simple example to introduce JMockit's state-based Mock. The tested code is as follows:

public class StateMocked {  

    public static int getDouble(int i){  
        return i*2;  
    }  

    public int getTriple(int i){  
        return i*3;  
    }  
} 

Rewrite the content of the ordinary method:

@Test  
public void testMockNormalMethodContent() throws IOException {  
    StateMocked obj = new StateMocked();  
    new MockUp<StateMocked>() {//使用MockUp修改被测试方法内部逻辑  
        @Mock  
      public int getTriple(int i) {  
            return i * 30;  
        }  
    };  
    assertEquals(30, obj.getTriple(1));  
    assertEquals(60, obj.getTriple(2));  
    Mockit.tearDownMocks();//注意:在JMockit1.5之后已经没有Mockit这个类,使用MockUp代替,mockUp和tearDown方法在MockUp类中  
}  

Modify the content of the static method: JMockit rewriting the content of the static/final method based on the state is no different from testing the normal method. It should be noted that the method in MockUp has the same signature as the Mocked method except that it does not contain the static keyword. , and use @Mock annotation, the test code is as follows:

@Test  
    public void testGetTriple() {  
        new MockUp<StateMocked>() {  
            @Mock    
            public int getDouble(int i){    
                return i*20;    
            }  
        };    
        assertEquals(20, StateMocked.getDouble(1));    
        assertEquals(40, StateMocked.getDouble(2));   
    }  

Guess you like

Origin blog.csdn.net/zy_dreamer/article/details/132307544