1. What is Mock
Project in each module, each class will have between interdependent relationship, in unit tests, we only care about the unit being tested for its dependent units are not concerned (there will be another test for that unit).
For example, a Class A logical layer number extraction method dependent data access layer class B, then processing logic. In the A unit testing, we are concerned about the return of the query results in different time B, A is how to deal with, rather than B in the end is how to take a number, how packaged as a model and so on.
Therefore, to block out the external dependencies, and Mock let us have a simulation environment.
Currently there are several industry Mock, here the most comprehensive selection of JMockit summarized.
2. JMockit Profile
JMockit works asm is modified by the original class bytecode, and then replace the contents of an existing class using the instrument jdk mechanism, so as to achieve the purpose of the mock.
JMockit used here is version 1.21, the specific use may not the same as with other versions, but the idea is the same. Maven configuration is as follows:
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.21</version>
<scope>test</scope>
</dependency>
JMockit There are two test methods for a behavior-based, one is based on the state tests.
1) Behavior-oriented(Expectations & Verifications)
2)State-oriented(MockUp<GenericType>)
Popular speak, Behavior-oriented behavior is based on mock, mock object code of behavior to imitate, more like a black box . State-oriented state is based on mock, standing inside the target test code. May be made to the parameters passed inspection, the match will return something similar to white box. State-oriented but basically the new MockUp mock any code or logic.
Suppose there are two classes, Service and DAO. Service database query by a number of different groups of goods, whether the goods get sold.
Package com.khlin.test.junit.jmockit.demo; public class -Service { Private the DAO DAO; public void setDao (the DAO DAO) { the this .dao = DAO; } / ** * The inventory is determined whether the goods sold * @param Group * @return * / public the Status the checkStatus (String Group) { int COUNT = the this .dao.getStoreCount (Group); IF (COUNT <= 0 ) { return Status.UNKOWN; } the else IF (COUNT <= 800) { return Status.UNSALABLE; } else if (count <= 1000) { return Status.NORMAL; } else { return Status.SELLINGWELL; } } }
package com.khlin.test.junit.jmockit.demo; import java.util.HashMap; import java.util.Map; public class DAO { private Map<String, Integer> groupCounts = new HashMap<String, Integer>(); /** * 假数据 */ { this.groupCounts.put("A", 500); this.groupCounts.put("B", 1000); this.groupCounts.put("C", 1200); } public int getStoreCount(String group) { Integer count = this.groupCounts.get(group); return null == count ? -1 : count.intValue(); } }
Package com.khlin.test.junit.jmockit.demo; public enum the Status { / ** * sold * / SELLINGWELL, / ** * General * / the NORMAL, / ** * unmarketable * / UNSALABLE, / ** * status unknown * / UNKOWN }
Mock behavior-based test, a total of three stages: record, replay, verify.
1) record: At this stage, a variety of methods expected to be called will be recorded in the actual implementation.
2) repaly: At this stage, unit testing Case, originally recorded in the record stage of the calls may have the opportunity to be executed. There is "likely" to emphasize that not recorded it will be strictly enforced.
3) verify: At this stage, the results of tests or other assertion is whether the original would expect.
Suppose now I just want to test Service, in the case of inventory 900 whether the state can return to NORMAL correct. Well, I do not care about in the end is what DAO incoming packet, nor how to care DAO database access, I just want to let DAO return 900, so that you can test the Service.
Sample code:
@RunWith (. JMockit class ) public class ServiceBehavier { @Mocked the DAO DAO = new new the DAO (); Private -Service-Service = new new -Service (); @Test public void Test () { // 1. Record Record expectation new new NonStrictExpectations () { { / ** * the method of recording * / dao.getStoreCount (AnyString); // the mock this method, whatever the value of type String passed, return the same value, to the effect of the black box / ** * expected results, Back 900 * / Result = 900; / ** Times must be called twice. In Expectations must be called, otherwise it will error, there is no need for calibration. In NonStrictExpectations not mandatory requirements, but it seems to be verified has verify the mandatory. Further there maxTimes, minTimes * / Times =. 1 ; } }; service.setDao (DAO); // 2. Replay call Assert.assertEquals (Status.NORMAL, service.checkStatus ( "D" )); // Assert.assertEquals (Status.NORMAL, service.checkStatus ( "D")); // 3. Is the check only called once. If the above statement is a comment and then transferred once, and the recorded times to 2, it will be an error in the validation stage. new new Verifications () { { dao.getStoreCount (AnyString); times = 1; } }; } }
Mock test based on the state of
By MockUp class of direct overwrite code logic mock classes, somewhat similar to the white box.
public class ServiceState { private DAO dao; private Service service; @Test public void test() { //1. mock对象 MockUp<DAO> mockUp = new MockUp<DAO>() { @Mock public int getStoreCount(String group) { return 2000; } }; //2. 获取实例 dao = mockUp.getMockInstance(); service = new Service(); service.setDao(dao); //3. Call Assert.assertEquals (Status.SELLINGWELL, service.checkStatus ( "FFF" )); // 4. restored objects, avoiding mutual influence between test method. In fact, no impact on one instance, the greater impact on the static method. Old version tearDown () method is static Mockit class mockUp.tearDown (); } }
3. JMockit mock sample code types or methods
Abstract class
package com.khlin.test.junit.jmockit.demo.jmockit; public abstract class AbstractA { public abstract int getAbstractAnything(); public int getAnything() { return 1; } }
Interface classes
package com.khlin.test.junit.jmockit.demo.jmockit; public interface InterfaceB { public int getAnything(); }
General Category
package com.khlin.test.junit.jmockit.demo.jmockit; public class ClassA { InterfaceB interfaceB; private int number; public void setInterfaceB(InterfaceB interfaceB) { this.interfaceB = interfaceB; } public int getAnything() { return getAnythingPrivate(); } private int getAnythingPrivate() { return 1; } public int getNumber() { return number; } public static int getStaticAnything(){ return getStaticAnythingPrivate(); } private static int getStaticAnythingPrivate() { return 1; } public int getClassBAnything() { return this.interfaceB.getAnything(); } }
Interface implementation class
package com.khlin.test.junit.jmockit.demo.jmockit; public class ClassB implements InterfaceB { public int getAnything() { return 10; } }
The ultimate test code
package com.khlin.test.junit.jmockit.demo; import mockit.Deencapsulation; import mockit.Expectations; import mockit.Injectable; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; import mockit.NonStrictExpectations; import mockit.Tested; import mockit.Verifications; import mockit.integration.junit4.JMockit; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import com.khlin.test.junit.jmockit.demo.jmockit.AbstractA; import com.khlin.test.junit.jmockit.demo.jmockit.ClassA; import com.khlin.test.junit.jmockit.demo.jmockit.ClassB; import com.khlin.test.junit.jmockit.demo.jmockit.InterfaceB; @RunWith(JMockit.class) public class JMockitTest { /** * mock私有方法 */ @Test public void testPrivateMethod() { final ClassA a = new ClassA(); // 局部参数,把a传进去 new NonStrictExpectations(a) { { Deencapsulation.invoke(a, "getAnythingPrivate"); result = 100; times = 1; } }; Assert.assertEquals(100, a.getAnything()); new Verifications() { { Deencapsulation.invoke(a, "getAnythingPrivate"); times = 1; } }; } /** * mock私有静态方法 */ @Test public void testPrivateStaticMethod() { new NonStrictExpectations(ClassA.class) { { Deencapsulation .invoke(ClassA.class, "getStaticAnythingPrivate"); result = 100; times = 1; } }; Assert.assertEquals(100, ClassA.getStaticAnything()); new Verifications() { { Deencapsulation .invoke(ClassA.class, "getStaticAnythingPrivate"); times = 1; } }; } / ** * mock公有方法 * / @Test public void testPublicMethod () { final ClassA classes = new ClassA (); new NonStrictExpectations (classes) { { classA.getAnything (); result = 100 ; times = 1 ; } }; Assert.assertEquals ( 100 , classA.getAnything ()); new verification () { { classA.getAnything (); times = 1 ; } }; } / ** * the mock public static method - based Behavior * / @Test public void testPublicStaticMethod () { new new NonStrictExpectations (of ClassA. Class ) { { ClassA.getStaticAnything (); Result = 100 ; Times =. 1 ; } }; Assert.assertEquals ( 100 , ClassA.getStaticAnything ()); new new Verifications () { { ClassA.getStaticAnything(); Times ; =. 1 } }; } / ** * the mock public static methods - based on the status * / @Test public void testPublicStaticMethodBaseOnStatus () { MockUp <of ClassA> = MockUp new new MockUp <of ClassA> () { @Mock public int getStaticAnything () { // Note that not declared as static return 100 ; } }; Assert.assertEquals ( 100 , ClassA.getStaticAnything ()); } / ** * the mock interfaces * / @Test public void testInterface() { InterfaceB interfaceB = new MockUp<InterfaceB>() { @Mock public int getAnything() { return 100; } }.getMockInstance(); ClassA classA = new ClassA(); classA.setInterfaceB(interfaceB); Assert.assertEquals(100, classA.getClassBAnything()); } /** * mock接口--基于状态 */ @Test public void testInterfaceBasedOnStatus() { final InterfaceB interfaceB = new ClassB(); new NonStrictExpectations(interfaceB) { { interfaceB.getAnything(); result = 100; times = 1; } }; ClassA classA = new ClassA(); classA.setInterfaceB(interfaceB); Assert.assertEquals(100, classA.getClassBAnything()); new Verifications() { { interfaceB.getAnything(); times = 1; } }; } /** * mock抽象类 */ @Test public void testAbstract() { AbstractA abstractA = new MockUp<AbstractA>() { @Mock public int getAbstractAnything(){ return 100; } @Mock public int getAnything(){ return 1000; } }.getMockInstance(); Assert.assertEquals(100, abstractA.getAbstractAnything()); Assert.assertEquals(1000, abstractA.getAnything()); } }