Mockito and PowerMock

Mockito and PowerMock are both popular Mock frameworks in Java. Using mocks allows us to isolate external dependencies so that we can unit test our own business logic code. When we write unit tests, we do not need to do cumbersome initialization work. Simulate a false method directly and specify the return value of the method arbitrarily. Mockito works by creating a proxy that depends on the object. All calls go through the proxy object, and the proxy object intercepts all requests and then processes them according to the preset return value. PowerMock is an extension of Mockito by modifying class bytecode and using custom ClassLoader to implement the functions of mock static methods, final methods, private methods and system classes. As you can see from the structure of both projects, PowerMock directly relies on Mockito, so there is no need to import a separate Mockito package if PowerMock has already been imported into your project. If you import both at the same time, there are compatibility issues between PowerMock and Mockito versions:

  1. Mockito package dependencies:

<dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <version>2.23.0</version>
        <scope>test</scope>
</dependency>
Copy the code
  1. PowerMock package dependencies:

<dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-junit4</artifactId>
        <version>2.0.0 - RC. 3</version>
        <scope>test</scope>
</dependency>
<dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-api-mockito2</artifactId>
        <version>2.0.0 - RC. 3</version>
        <scope>test</scope>
</dependency>
Copy the code

The use of Mockito

Mockito typically implements the function of simulation by creating mock or Spy objects and setting specific return rules. After the call is complete, method call validation can be performed to verify that the program logic is correct. The difference between mock and Spy objects is that mock objects return the default value of the method value type for calls without specified processing rules (for example, int, long returns 0, Boolean returns false, object returns null, and void does nothing). The Spy object, on the other hand, calls the real method directly when no processing rules are specified.

The following three classes are some of the business classes we need to use in our project:

/ / entity class
public class Node {
    private int num;
    private String name;

    // Ignore all constructors and get and set methods
}

// The local business class responsible for implementing the specific business
public class LocalServiceImpl implements ILocalService {

    // External dependencies
    @Autowired
    private IRemoteService remoteService;

    // Specific business processing methods
    @Override
    public Node getRemoteNode(int num) {
        return remoteService.getRemoteNode(num);
    }
    // The other business invocation methods are ignored below and will be added in the following examples
}

// Externally dependent business classes are implemented by others. Perhaps our business classes have not been written by others
public class RemoteServiceImpl implements IRemoteService {
    // Some business methods provided by the external class
    @Override
    public Node getRemoteNode(int num) {
        return new Node(num, "Node from remote service");
    }
    // Other business methods will be added in the following examples
}
Copy the code

Here are some examples of Mockito in action:

  1. Mock an external dependency object and inject it into our business class for mock invocation in unit tests:
@RunWith(MockitoJUnitRunner.class) // Let the test run in Mockito
public class LocalServiceImplMockTest {

    @InjectMocks    // This annotation indicates that the object needs to be mock
    private LocalServiceImpl localService;
    @Mock   // This annotation will automatically create a mock object and inject it into the @injectMocks object
    private RemoteServiceImpl remoteService;

    // If you don't use the above annotations, you can use the @before method to manually create and inject mock objects, but it takes a few lines of code
    /* private LocalServiceImpl localService; private RemoteServiceImpl remoteService; @Before public void setUp() throws Exception { localService = new LocalServiceImpl(); remoteService = Mockito.mock(RemoteServiceImpl.class); / / create a Mock object Whitebox. SetInternalState (localService "remoteService", remoteService); // Inject the dependent object} */

    @Test
    public void testMock(a) {
        Node target = new Node(1."target");    // Create a Node object as return value
        Mockito.when(remoteService.getRemoteNode(1)).thenReturn(target); / / specified when remoteService. GetRemoteNode (int) method introduced to return to the target object parameter to 1
        Node result = localService.getRemoteNode(1);    // Call our business methods, which internally call dependent object methods
        assertEquals(target, result);   // We can assert that the return value is the target object
        assertEquals(1, result.getNum());   // The concrete attribute is the same as the return value we specify
        assertEquals("target", result.getName());   // The concrete attribute is the same as the return value we specify

        Node result2 = localService.getRemoteNode(2);   // The return rule is not specified when the parameter is 2
        assertNull(result2);    // Null is returned if not specified}}Copy the code
  1. Spy external dependency objects and inject them into our business class:
@RunWith(MockitoJUnitRunner.class)
public class LocalServiceImplSpyTest {

    @InjectMocks
    private LocalServiceImpl localService;
    @Spy    // Note that the @spy annotation is used
    private RemoteServiceImpl remoteService;
    // If you want to create your own spy object, write this:
    /* remoteService = new RemoteServiceImpl(); Mocteservice = mockito.spy (remoteService); // Then call mockito.spy (T) to create a Spy object */

    @Test
    public void testSpy(a) {
        Node target = new Node(1."target");    // Create a Node object as return value
        Mockito.when(remoteService.getRemoteNode(1)).thenReturn(target); / / specified when remoteService. GetRemoteNode (int) method introduced to return to the target object parameter to 1
        Node result = localService.getRemoteNode(1);    // Call our business methods, which internally call dependent object methods
        assertEquals(target, result);   // We can assert that the return value is the target object
        assertEquals(1, result.getNum());   // The concrete attribute is the same as the return value we specify
        assertEquals("target", result.getName());   // The concrete attribute is the same as the return value we specify

        Node result2 = localService.getRemoteNode(2);   // No rule is specified for calling with argument 2, so the actual object is called directly, returning the node created by remote
        assertEquals(2, result2.getNum());
        assertEquals("Node from remote service", result2.getName());    //remoteService set name to "Node from remote service" when creating Node object}}Copy the code
  1. Use the Any series of methods of ArgumentMatchers to specify multiple return values. There are any(), anyInt(), anyString(), anyByte(), anyLong(), and so on. Look at all the methods defined in the ArgumentMatchers class source code:
    @Test
    public void testAny(a) {
        Node target = new Node(1."target");
        when(remoteService.getRemoteNode(anyInt())).thenReturn(target); // Import mockito. when and ArgumentMatchers. AnyInt statically to simplify the code and improve readability

        Node result = localService.getRemoteNode(20); / / the above specifies the call remoteService. GetRemoteNode (int), no matter what the incoming parameters would return to the target object
        assertEquals(target, result);   // We can assert that the return value is the target object
        assertEquals(1, result.getNum());   // The concrete attribute is the same as the return value we specify
        assertEquals("target", result.getName());   // The concrete attribute is the same as the return value we specify
    }
Copy the code
  1. Specifies the return value of multiple calls to the mock object:
    /** * specifies the return value */ for multiple mock calls
    @Test
    public void testMultipleReturn(a) {
        Node target1 = new Node(1."target");
        Node target2 = new Node(1."target");
        Node target3 = new Node(1."target");
        when(remoteService.getRemoteNode(anyInt())).thenReturn(target1).thenReturn(target2).thenReturn(target3);
        // The first call returns target1, the second targeT2, and the third target3

        Node result1 = localService.getRemoteNode(1); // The first call
        assertEquals(target1, result1);
        Node result2 = localService.getRemoteNode(2); // The second call
        assertEquals(target2, result2);
        Node result3 = localService.getRemoteNode(3); // The third call
        assertEquals(target3, result3);
    }
Copy the code
  1. Specify the mock object throwing an exception (note that if the method statement throws an exception, not only to specify a runtime exception is thrown, if specified as still throw exceptions to be examined, runtime to error org. Mockito. Exceptions. Base. MockitoException: Checked exception is invalid for this method!) :
    / / RemoteServiceImpl method:
    @Override
    public Node getRemoteNode(String name) throws MockException {
        if (StringUtils.isEmpty(name)) {
            throw new MockException("Name cannot be empty.", name);
        }
        return new Node(name);
    }

    / / LocalServiceImpl method
    @Override
    public Node getRemoteNode(String name) throws MockException {
        try {
            return remoteService.getRemoteNode(name);
        } catch (IllegalArgumentException e) {
            throwe; }}/** * The method that specifies the mock object to throw declared exceptions throws checked exceptions */
    @Test
    public void testExceptionDeclare(a) {
        try {
            Node target = new Node(1."target");
            when(remoteService.getRemoteNode("name")).thenReturn(target).thenThrow(new MockException(
                    "message"."exception")); // The first call returns normally, and the second throws an Exception

            Node result1 = localService.getRemoteNode("name");
            assertEquals(target, result1); // The first call returns normally

            Node result2 = localService.getRemoteNode("name"); // The second call will not return normally and will throw an exception
            assertEquals(target, result2);
        } catch (MockException e) {
            assertEquals("exception", e.getName()); // Verify that the specified exception is returned
            assertEquals("message", e.getMessage()); // Verify that the specified exception is returned}}/** * Specifies a mock object to throw a runtime exception for the method that declares the exception to be thrown */
    @Test
    public void testRuntimeException(a) {
        Node target = new Node(1."target");
        when(remoteService.getRemoteNode(1)).thenThrow(new RuntimeException("exception")); // Specifies that a runtime exception is thrown on the call

        try {
            Node result = localService.getRemoteNode(1);
            assertEquals(target, result);
        } catch (RuntimeException e) {
            assertEquals("exception", e.getMessage()); }}/** * specifies that any method that does not declare an exception to throw a mock object throws a checked exception
    @Test
    public void testNotDefineCheckedException(a) {
        Node target = new Node(1."target");
        when(remoteService.getRemoteNode(1)).thenThrow(new IOException("io exception"));

        try {
            Node result = localService.getRemoteNode(1);
            assertEquals(target, result);
        } catch (Exception e) {
            assertEquals("io exception", e.getMessage()); }}Copy the code
  1. The mock void method throws an exception and does nothing:
    / / RemoteServiceImpl method:
    @Override
    public void doSometing(a) {
        System.out.println("remote service do something!");
    }

    / / LocalServiceImpl method
    @Override
    public void remoteDoSomething(a) {
        remoteService.doSometing();
    }

    // Note that the void method does not return a value, so the mock rules are written in a different order
    doNothing().when(remoteService).doSometing();
    doThrow(new RuntimeException("exception")).when(remoteService).doSometing();
Copy the code
  1. Mockito: Mockito: Mockito: Mockito: Mockito: Mockito: Mockito: Mockito: Mockito: Mockito: Mockito: Mockito
   /** * Verify mock object and method calls ** /
    public void testVerify(a) {
        Node target = new Node(1."target");
        when(remoteService.getRemoteNode(anyInt())).thenReturn(target);

        verify(remoteService, never()).getRemoteNode(1); // The mock method is not called

        localService.getRemoteNode(1);
        Mockito.verify(remoteService, times(1)).getRemoteNode(anyInt()); The mock method has been called once

        localService.getRemoteNode(2);
        verify(remoteService, times(2)).getRemoteNode(anyInt()); The mock method has been called twice
        verify(remoteService, times(1)).getRemoteNode(2); The mock method has arguments of 2 and has been called only once
    }
Copy the code
  1. Mock method argument verification using ArgumentCaptor to capture method arguments
    /** * Mock method argument validation using ArgumentCaptor to capture method arguments */
    @Test
    public void testCaptor(a) throws Exception {
        Node target = new Node(1."target");
        when(remoteService.getRemoteNode(anyString())).thenReturn(target);

        localService.getRemoteNode("name1");
        localService.getRemoteNode("name2");
        verify(remoteService, atLeastOnce()).getRemoteNode(localCaptor.capture()); / / set the captor

        assertEquals("name2", localCaptor.getValue()); // Get the parameters of the last call
        List<String> list = localCaptor.getAllValues(); // Get all the passed arguments in order
        assertEquals("name1", list.get(0));
        assertEquals("name2", list.get(1));
    }
Copy the code
  1. Mock objects call real methods:
    Mock objects call real methods */
    @Test
    public void testCallRealMethod(a) {
        when(remoteService.getRemoteNode(anyInt())).thenCallRealMethod(); // Set to call the real method
        Node result = localService.getRemoteNode(1);

        assertEquals(1, result.getNum());
        assertEquals("Node from remote service", result.getName());
    }
Copy the code
  1. Reset the mock object:
    // Reset the mock to clear all call records and return rules
    Mockito.reset(remoteService);
Copy the code
  1. Verify mock object 0 calls against unvalidated calls
    /** * Verify mock object 0 calls and non-validated calls */
    @Test(expected = NoInteractionsWanted.class)
    public void testInteraction(a) {
        verifyZeroInteractions(remoteService); // It has not been called yet

        Node target = new Node(1."target");
        when(remoteService.getRemoteNode(anyInt())).thenReturn(target);

        localService.getRemoteNode(1);
        localService.getRemoteNode(2);
        verify(remoteService, times(2)).getRemoteNode(anyInt());
        // Both calls with arguments 1 and 2 are checked by anyInt() above, so there are no unchecked calls
        verifyNoMoreInteractions(remoteService);

        reset(remoteService);
        localService.getRemoteNode(1);
        localService.getRemoteNode(2);
        verify(remoteService, times(1)).getRemoteNode(1);
        // The call with argument 2 is not checked by the above, so the execution will throw an exception
        verifyNoMoreInteractions(remoteService);
    }
Copy the code

Use of PowerMock

There are a few differences between the use of PowerMock and Mockito. The first is that the @RunWith annotation on the test class needs to be changed to:

@RunWith(PowerMockRunner.class)
Copy the code

The second is to use the @PrepareForTest annotation (PrepareFotTest annotation modifies the bytecode of the class passed in to simulate final, static, private methods, system classes, etc.). This annotation can be written on a class or method:

@PrepareForTest(RemoteServiceImpl.class)
Copy the code
  1. Mock New keyword
    //LocalServiceImpl
    @Override
    public Node getLocalNode(int num, String name) {
        return new Node(num, name);
    }

    ** * mock new keyword */
    @Test
    @PrepareForTest(LocalServiceImpl.class) / / PrepareForTest modify the local class bytecode to cover new features
    public void testNew(a) throws Exception {
        Node target = new Node(1."target");
        // If any int is passed and the name property is "name", the new object is returned as target
        Eq ("name") indicates that the parameter is equal to "name"
        // The remaining methods are isNull(), isNotNull(), isA(), etc
        PowerMockito.whenNew(Node.class).withArguments(anyInt(), eq("name")).thenReturn(target);
        Node result = localService.getLocalNode(2."name");
        assertEquals(target, result); // Return target
        assertEquals(1, result.getNum());
        assertEquals("target", result.getName());

        // If name is "test", null is returned by default
        Node result2 = localService.getLocalNode(1."test");
        assertNull(result2);
    }
Copy the code
  1. The mock final method
    //RemoteServiceImpl
    @Override
    public final Node getFinalNode(a) {
        return new Node(1."final node");
    }

    Mock final method */
    @Test
    @PrepareForTest(RemoteServiceImpl.class) // Final methods are in the RemoteServiceImpl class
    public void testFinal(a) {
        Node target = new Node(2."mock");
        PowerMockito.when(remoteService.getFinalNode()).thenReturn(target); // Specify the return value

        Node result = remoteService.getFinalNode(); // Call the final method directly and return the mock value
        assertEquals(target, result); // Verify the return value
        assertEquals(2, result.getNum());
        assertEquals("mock", result.getName());
    }
Copy the code
  1. Mock the static method
    //Node
    public static Node getStaticNode(a) {
        return new Node(1."static node");
    }

    /** * Mock static */
    @Test
    @PrepareForTest(Node.class) // Static methods are defined in the Node class
    public void testStatic(a) {
        Node target = new Node(2."mock");
        PowerMockito.mockStatic(Node.class); // Mock Static method
        PowerMockito.when(Node.getStaticNode()).thenReturn(target); // Specify the return value

        Node result = Node.getStaticNode(); // Call the static method directly and return the mock value
        assertEquals(target, result); // Verify the return value
        assertEquals(2, result.getNum());
        assertEquals("mock", result.getName());
    }
Copy the code
  1. The mock private methods
    //RemoteServiceImpl
    @Override
    public Node getPrivateNode(a) {
        return privateMethod();
    }

    //RemoteServiceImpl
    private Node privateMethod(a) {
        return new Node(1."private node");
    }

    Mock private methods */
    @Test
    @PrepareForTest(RemoteServiceImpl.class) // Private methods are defined in the RemoteServiceImpl class
    public void testPrivate(a) throws Exception {
        Node target = new Node(2."mock");
        // Call the privateMethod method in real code
        PowerMockito.when(remoteService.getPrivateNode()).thenCallRealMethod();
        // The private method cannot be accessed. Pass the method name and parameters like reflection
        PowerMockito.when(remoteService, "privateMethod").thenReturn(target);

        Node result = remoteService.getPrivateNode();
        assertEquals(target, result); // Verify the return value
        assertEquals(2, result.getNum());
        assertEquals("mock", result.getName());
    }
Copy the code
  1. Mock system class methods
    //RemoteServiceImpl
    @Override
    public Node getSystemPropertyNode(a) {
        return new Node(System.getProperty("abc"));
    }

    /** mock system class method */
    @Test
    @PrepareForTest(RemoteServiceImpl.class) // Like the new keyword, the system class method is called in the RemoteServiceImpl class, so fill in the RemoteServiceImpl
    public void testSystem(a) {
        PowerMockito.mockStatic(System.class); // The static method of the system class is called, so add this sentence
        PowerMockito.when(System.getProperty("abc")).thenReturn("mock"); // Set system.getProperty (" ABC ") to return "mock"
        PowerMockito.when(remoteService.getSystemPropertyNode()).thenCallRealMethod(); // Set the mock object to call the actual method

        Node result = remoteService.getSystemPropertyNode(); // The code will return an object whose name attribute is "mock"
        assertEquals(0, result.getNum()); //int Defaults to 0
        assertEquals("mock", result.getName()); // Calling System.getProperty(" ABC ") from the remoteService object returns the "mock" set above
    }
Copy the code

The project code is uploaded to GitHub: MockDemo


Welcome to scan the code to follow my wechat official account: magicTan.