I tried to organize the proper use of Mockito and PowerMock when mocking various methods when testing with JUnit with Spring Boot.
build.gradle
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile group: 'org.powermock', name: 'powermock-api-mockito', version: '1.6.6'
testCompile group: 'org.powermock', name: 'powermock-module-junit4', version: '1.6.6'
}
Just put spring-boot-starter-test and you can use the following two libraries. The version is ** spring-boot-gradle-plugin: 1.5.2.RELEASE **.
Add a setting to use the power mock that will be used later.
TestTargetClass.java
@Component
public class TestTargetClass {
@Autowired
private TestSubClass testSubClass;
private String private_method() {
return "private_method called " + private_sub();
}
private String private_sub() {
return "private_sub";
}
public String public_method() {
return "public_method called " + public_sub();
}
public String public_sub() {
return "public_sub";
}
public String public_method_call_private_method() {
return "public_method called " + private_sub();
}
public static String static_method() {
return "static_method";
}
public String public_subclass_public_method() {
return "public_subclass_method called " + testSubClass.sub_public_method();
}
public String public_subclass_private_method() {
return "public_subclass_method called " + testSubClass.sub_public_method_call_private_method();
}
}
TestSubClass.java
@Component
public class TestSubClass {
public String public_field = "public_field";
private String private_field = "private_field";
private String sub_private_method() {
return "subclass_private_method has " + private_field;
}
public String sub_public_method() {
return "subclass_public_method has " + public_field;
}
public String sub_public_method_call_private_method() {
return "subclass_public_method called " + sub_private_method();
}
}
You can use Mockito for the test class like this.
RealTest.java
@RunWith(SpringRunner.class)
@SpringBootTest
public class RealTest {
@Autowired
private TestTargetClass instance;
@Test
public void test method() {
.....
}
}
Needless to write, here is the test code
@Test
public void can be tested by calling public methods() {
assertThat(public_method(), is("public_method called public_sub"));
}
OK if you use setAccessible with reflection
@Test
public void can be tested by calling private methods() throws Exception {
Method method = TestTargetClass.class.getDeclaredMethod("private_method");
method.setAccessible(true);
assertThat((String) method.invoke(instance), is("private_method called private_sub"));
}
Needless to write, here is the test code
@Test
can be tested by calling public void static methods() {
assertThat(TestTargetClass.static_method(), is("static_method"));
}
Here is the test using the mock of the main subject.
In such cases, use Mockito.mock ()
.
@Test
public void can mock public methods() {
TestTargetClass mockInstance = mock(TestTargetClass.class);
when(mockInstance.public_method()).thenReturn("mocked_public_method");
assertThat(mockInstance.public_method(), is("mocked_public_method"));
}
I want the methods other than the mocking method public_sub ()
to work as implemented, so use Mockito.spy ()
in such cases.
@Test
public void Sub methods called by public methods can be mocked with Spy() {
TestTargetClass mockInstance = spy(new TestTargetClass());
when(mockInstance.public_sub()).thenReturn("mocked_public_sub");
assertThat(mockInstance.public_method(), is("public_method called mocked_public_sub"));
}
The test class when using PowerMock looks like this
@RunWith(PowerMockRunner.class)
@PrepareForTest({TestTargetClass.class})
public class PowerMockTest {
@Test
public void test method() {
.....
}
}
Use PowerMockito.mock ()
and PowerMockito.when ()
. Also, to specify a private method, use MemberMatcher.method ()
to reflect.
@Test
public void private methods can be mocked with PowerMockito() throws Exception {
TestTargetClass mockInstance = PowerMockito.mock(TestTargetClass.class);
PowerMockito.when(mockInstance, MemberMatcher.method(TestTargetClass.class, "private_method"))
.withNoArguments().thenReturn("mocked_private_method");
//Use reflection to access private methods
Method method = TestTargetClass.class.getDeclaredMethod("private_method");
method.setAccessible(true);
assertThat((String) method.invoke(mockInstance), is("mocked_private_method"));
}
Use PowerMockito.mockStatic ()
and PowerMockito.when ()
.
@Test
public void static methods can be mocked with PowerMockito()() {
PowerMockito.mockStatic(TestTargetClass.class);
PowerMockito.when(TestTargetClass.static_method()).thenReturn("mocked_static_method");
assertThat(TestTargetClass.static_method(), is("mocked_static_method"));
}
Since we want methods other than the mocking method private_sub ()
to work as implemented, use PowerMockito.spy ()
and PowerMockito.when ()
in such cases. Also, to specify a private method, use MemberMatcher.method ()
to reflect.
@Test
public void Sub-private methods called from public methods can be mocked with spy() throws Exception {
TestTargetClass mockInstance = PowerMockito.spy(new TestTargetClass());
PowerMockito.when(mockInstance, MemberMatcher.method(TestTargetClass.class, "private_sub"))
.withNoArguments().thenReturn("mocked_private_sub");
assertThat(mockInstance.public_method_call_private_method(),
is("public_method called mocked_private_sub"));
}
It is assumed that the service class that is DI in the controller class is mocked.
PowerMock is used at this time as well.
DI the target class of @ Autowired
specified in the class specified by @InjectMocks
in the @SpringBootTest
annotation.
The target class of @ Autowired
isPowerMockito.spy ()
as a class variable.
The test class looks like this
@RunWith(PowerMockRunner.class)
//PowerMock target class
@PrepareForTest({TestSubClass.class})
@SpringBootTest
public class PowerMockSubclassTest {
@InjectMocks
private TestTargetClass instance;
private TestSubClass testSubClass = PowerMockito.spy(new TestSubClass());
@Test
public void test method() {
.....
}
}
It seems that you can not mock even if you do the following instead of the class variable. ... Perhaps Please comment if there is any good way.
@Test
public void test method() {
TestSubClass testSubClass = PowerMockito.spy(new TestSubClass());
}
Since it is actually a public method, the part where the subclass is declared in the test class is
private TestSubClass testSubClass = PowerMockito.mock(TestSubClass.class);
However, it cannot be used together with the one using spy () in the same source file, so unify it with spy ().
Use PowerMockito.when ()
.
@Test
public void You can mock public methods of subclasses() {
PowerMockito.when(testSubClass.sub_public_method()).thenReturn("mocked_subclass_public_method");
assertThat(instance.public_subclass_public_method(),
is("public_subclass_method called mocked_subclass_public_method"));
}
Use PowerMockito.when ()
. Also, to specify a private method, use MemberMatcher.method ()
to reflect.
@Test
public void Can mock private methods of subclasses() throws Exception {
PowerMockito.when(testSubClass, MemberMatcher.method(TestSubClass.class, "sub_private_method"))
.withNoArguments().thenReturn("mocked_sub_private_method");
assertThat(instance.public_subclass_private_method(), is(
"public_subclass_method called subclass_public_method called mocked_sub_private_method"));
}
Use Whitebox.setInternalState ()
to mock a subclass field.
@Test
public void You can mock public fields in subclasses() {
Whitebox.setInternalState(testSubClass, "public_field", "mocked_public_field");
assertThat(instance.public_subclass_public_method(),
is("public_subclass_method called subclass_public_method has mocked_public_field"));
}
Use Whitebox.setInternalState ()
to mock a subclass field.
Same as setting it in the public field.
@Test
public void Can mock private fields in subclasses() {
Whitebox.setInternalState(testSubClass, "private_field", "mocked_private_field");
assertThat(instance.public_subclass_private_method(), is(
"public_subclass_method called subclass_public_method called subclass_private_method has mocked_private_field"));
}
It's a long sentence, but that's it.
Recommended Posts