How to test a private method in Java and partially mock that method

Overview

When I wrote a test with Spring Boot, I didn't know how to test the private method and how to mock the private method, so I will summarize it. It seems that you cannot mock private methods with Mockito alone, so use PowerMock together.

environment

Tested class

Prepare a class to be tested appropriately. Let's call it the HelloService class.

import org.springframework.beans.factory.annotation.Autowired;

public class HelloService {

    @Autowired
    private HelloRepository repository;

    public String getFromRepo() {
        return repository.getData();
    }

    public String methodPublic(String arg) {
        return "method public: " + methodPrivate(arg);
    }

    private String methodPrivate(String arg) {
        return "method private, arg: " + arg;
    }
}

Create a class that the HelloService class depends on.

public class HelloRepository {

    public String getData() {
        return "HelloRepository.getData";
    }
}

Commentary

--The methodPrivate method is a private method. [Testing private methods](#private method testing) describes how to test this method. --The methodPublic method is a public method, and the private method is called inside. [Chapter of how to partially mock a private method](#Private method is partially mocked) explains how to partially mock a private method in the same class. --getFromRepo calls the method of the dependent HelloRepository. In [Chapter for using with Mockito's mock](using with #mockito's mock), test it in a way that coexists with the previous code.

Testing private methods

Here, the test method of the methodPrivate method, which is a private method of the above-mentioned test target class HelloService class, will be explained.

Try

import org.junit.Before;
import org.junit.Test;
import java.lang.reflect.Method;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

public class HelloServiceTest {

    private HelloService service;

    @Before
    public void setUp() {
        service = new HelloService();
    }

    @Test
Public void method Private test() throws Exception {
        Method method = HelloService.class.getDeclaredMethod("methodPrivate", String.class);
        method.setAccessible(true);
        String actual = (String)method.invoke(service, "hoge");
        assertThat(actual, is("method private, arg: hoge"));
    }
}

Commentary

--You can get the method instance of the test target class by using test target class.class.getDeclaredMethod (test target method name, argument type). --If you set method.setAccessible (true), you can also call it with a private method. --The method can be called with method.invoke (argument). Since the return value will be Object, cast it. --If you get a warning saying ʻUnchecked cast, add @SuppressWarnings ("unchecked") `in the line above and it will disappear

Partial mock a private method

Here, we will explain how to partially mock the private method methodPrivate called in the above when testing the methodPublic method of the test target class HelloService class.

Try

Since PowerMock is used, add the following to build.gradle (If you are using maven, please read as appropriate)

dependencies {
    testCompile('org.powermock:powermock-module-junit4:1.6.2')
    testCompile('org.powermock:powermock-api-mockito:1.7.3')
}

Write a test

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.lang.reflect.Method;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.times;
import static org.powermock.api.mockito.PowerMockito.verifyPrivate;
import static org.powermock.api.mockito.PowerMockito.when;
import static org.powermock.api.mockito.PowerMockito.spy;

@RunWith(PowerMockRunner.class)
@PrepareForTest(HelloService.class)
public class HelloServiceTest {

    private HelloService service;

    @Before
    public void setUp() {
        service = spy(new HelloService());
    }

    @Test
Public void method Public test() throws Exception {
        when(service, "methodPrivate", "fuga").thenReturn("hoge");
        String actual = service.methodPublic("fuga");
        verifyPrivate(service, times(1)).invoke("methodPrivate", "fuga");

        assertThat(actual, is("method public: " + "hoge"));
    }
}

Commentary

--Added two annotations to the class. @RunWith (PowerMockRunner.class) and @PrepareForTest (tested.class) --In order to create a partial mock, spy (instance of the class under test) is called insetUp (). The point here is to use PowerMock's spy (ʻorg.powermock.api.mockito.PowerMockito.spy) instead of Mockito's spy. --The same is true for when (), using PowerMock when. I'm using ʻorg.powermock.api.mockito.PowerMockito.when. ――You can make a partial mock without worrying about whether it is private or public. --Use verifyPrivate to verify that it was called. ――The usability of when and verify seems to be the same as Mockito

Used with Mockito mock

Add a test for the getFromRepo method.

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.powermock.api.mockito.PowerMockito.verifyPrivate;
import static org.powermock.api.mockito.PowerMockito.when;
import static org.powermock.api.mockito.PowerMockito.spy;

@RunWith(PowerMockRunner.class)
@PrepareForTest(HelloService.class)
public class HelloServiceTest {

    @Mock
    private HelloRepository repository;

    @InjectMocks
    private HelloService service;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        service = spy(service);
    }

    @Test
Public void getFromRepo test() {
        when(repository.getData()).thenReturn("hoge");
        assertThat(service.getFromRepo(), is("hoge"));

        verify(service, times(1)).getFromRepo();
    }

    @Test
Public void method Public test() throws Exception { //Same as above}

    @Test
Public void method Private test() throws Exception { //Same as above}
}

Commentary

--Mocking the dependent Hello Repository. --Annotate the field you want to mock with @ Mock and annotate the field you want to mock with @InjectMocks. --If you call MockitoAnnotations.initMocks (this); in setUp (), it seems that it will inject a mock. ――I was worried if it would coexist with the above PowerMock, but it worked fine if I simply did service = spy (service); after initMocks.

Impressions

PowerMock was convenient. I used Mockito when mocking non-private methods (like HelloRepository in this case), and I thought it would be useful for @ Mock or @InjectMocks. When I tried to use PowerMock to mock a private method, I thought it would be confusing if the method changed drastically, but I'm relieved that I can write it in the same way.

reference

Recommended Posts

How to test a private method in Java and partially mock that method
How to mock a super method call in PowerMock
How to convert A to a and a to A using AND and OR in Java
How to test a private method with RSpec for yourself
How to develop and register a Sota app in Java
How to implement a job that uses Java API in JobScheduler
How to display a web page in Java
How to access Java Private methods and fields
How to create a convenient method that utilizes generics and functional interfaces
How to ZIP a JAVA CSV file and manage it in a Byte array
How to create a Java environment in just 3 seconds
[Java] How to omit the private constructor in Lombok
How to create a data URI (base64) in Java
How to convert a file to a byte array in Java
How to create a method
How to store a string from ArrayList to String in Java (Personal)
What happened in "Java 8 to Java 11" and how to build an environment
Create a method to return the tax rate in Java
How to call and use API in Java (Spring Boot)
How to simulate uploading a post-object form to OSS in Java
Differences in how to handle strings between Java and Perl
How to make a Java container
[Java] How to use join method
How to learn JAVA in 7 days
How to batch initialize arrays in Java that I didn't know when I was a beginner
[Java] How to create a folder
How to use classes in Java?
How to name variables in Java
How to make a Java array
How to concatenate strings in java
How to POST JSON in Java-Method using OkHttp3 and method using HttpUrlConnection-
How to create a new Gradle + Java + Jar project in Intellij 2016.03
How to automatically operate a screen created in Java on Windows
How to store the information entered in textarea in a variable in the method
How to encrypt and decrypt with RSA public key in Java
[Wire Mock] I want to set up a stub / mock server in Java and perform E2E tests.
[Java] How to search for a value in an array (or list) with the contains method
How to make a key pair of ecdsa in a format that can be read by Java
If you want to mock a method in RSpec, you should use the allow method for mock and the singleton method.
How to make a Java calendar Summary
How to implement date calculation in Java
How to implement Kalman filter in Java
Multilingual Locale in Java How to use Locale
[Java] How to compare with equals method
How to filter JUnit Test in Gradle
How to insert a video in Rails
How to use submit method (Java Silver)
How to do base conversion in Java
[Introduction to Java] How to write a Java program
[Java] How to use the toString () method
How to have params in link_to method
[Java] How to output and write files!
How to make a Discord bot (Java)
How to implement coding conventions in Java
How to embed Janus Graph in Java
How to print a Java Word document
How to get the date in java
How to test private scope with JUnit
How to publish a library in jCenter
[SpringBoot] How to write a controller test
Java to C and C to Java in Android Studio