I tried to check Java jmockit before, but it doesn't fit a little, so it is a memo when I checked Mockito and Powermock as Plan B.
Mockito https://site.mockito.org/
Mockito JavaDoc https://javadoc.io/doc/org.mockito/mockito-core/3.1.0/org/mockito/Mockito.html
Powermock https://github.com/powermock/powermock/
** Verification environment ** java version "1.8.0_202" Eclipse IDE for Enterprise Java Developers. Version: 2019-03 (4.11.0) Build id: 20190314-1200 JUnit4 mockito-2.23 powermock-mockito2-2.0.2
Mockito is a library that provides mock to use when writing test code. As a limitation, you cannot mock constructors, static methods, or private methods.
PowerMock extends both EasyMock and Mockito with the ability to mock static, final, and even private methods. It is possible to work in combination with Mockito.
This time, I downloaded and used "powermock-mockito2-junit-2.0.2.zip" from the following. https://github.com/powermock/powermock/wiki/Downloads
Also, in this environment, an error occurred at runtime unless the following Jar was obtained and referenced separately. ・ Byte-buddy-1.9.10.jar ・ Byte-buddy-agent-1.9.10.jar https://howtodoinjava.com/mockito/plugin-mockmaker-error/
The example below is an example of creating a stub that returns a fixed value when a specific argument is passed to the mock method.
package powermockTest;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
public class Test001 {
class ClassA {
public int testInt(int x, int y) {
return x + y;
}
}
@Test
public void testWhen1() {
ClassA mockedA = mock(ClassA.class);
when(mockedA.testInt(5, 6)).thenReturn(99);
//Result specified by mock
assertEquals(99, mockedA.testInt(5, 6));
//Unmocked results
assertEquals(0, mockedA.testInt(5, 7));
}
}
The parameters specified when mocking can use argument matchers as well as fixed values. In the example below, you can use anyInt () instead of a fixed value to accept any integer.
@Test
public void testWhen2() {
ClassA mockedA = mock(ClassA.class);
//Conditions can be specified using the argument matcher.
//See below for other argument matchers
// https://javadoc.io/static/org.mockito/mockito-core/3.1.0/org/mockito/ArgumentMatchers.html
when(mockedA.testInt(anyInt(), anyInt())).thenReturn(99);
//Result specified by mock
assertEquals(99, mockedA.testInt(5, 6));
assertEquals(99, mockedA.testInt(5, 7));
}
You can also use the following argument matchers https://javadoc.io/static/org.mockito/mockito-core/3.1.0/org/mockito/ArgumentMatchers.html
Note that you cannot use argument matchers for only some of the arguments of a method that has multiple arguments. Use the argument matcher for all arguments. If you want to use a literal value, the implementation is as follows.
@Test
public void testWhen3() {
//If you use an argument matcher, all arguments must be provided by the matcher.
/* eq(5)If you specify 5 without using, the following error will occur.
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers!
2 matchers expected, 1 recorded:
*/
ClassA mockedA = mock(ClassA.class);
when(mockedA.testInt(eq(5), anyInt())).thenReturn(99);
//Result specified by mock
assertEquals(99, mockedA.testInt(5, 6));
assertEquals(99, mockedA.testInt(5, 7));
//Unmocked results
assertEquals(0, mockedA.testInt(6, 7));
}
In jmockit, if you have a class mocked using @Mocked, all instances created during that test will be mocked. mockito does not do the following:
@Test
public void testWhen4() {
ClassA mockedA = mock(ClassA.class);
when(mockedA.testInt(5, 6)).thenReturn(99);
//Result specified by mock
assertEquals(99, mockedA.testInt(5, 6));
//Unlike jmockit, it does not affect the next instance created.
ClassA objA = new ClassA();
assertEquals(11, objA.testInt(5, 6));
}
To return another result with the same argument, implement as follows.
//How to return different results with the same arguments
@Test
public void testWhen5() {
ClassA mockedA = mock(ClassA.class);
when(mockedA.testInt(5, 6))
.thenReturn(99)
.thenReturn(100);
//Result specified by mock
assertEquals(99, mockedA.testInt(5, 6));
assertEquals(100, mockedA.testInt(5, 6));
assertEquals(100, mockedA.testInt(5, 6));
//Another way of writing
when(mockedA.testInt(1, 2)).thenReturn(10,20,30);
assertEquals(10, mockedA.testInt(1, 2));
assertEquals(20, mockedA.testInt(1, 2));
assertEquals(30, mockedA.testInt(1, 2));
assertEquals(30, mockedA.testInt(1, 2));
}
If you implement the following, it will be overwritten with the contents described at the end.
when(mockedA.testInt(5, 6)).thenReturn(99); //This is ignored
when(mockedA.testInt(5, 6)).thenReturn(100);
To output an exception with a mocked method, implement the following.
@Test
public void testWhen6() {
ClassA mockedA = mock(ClassA.class);
doThrow(new IllegalArgumentException("test")).when(mockedA).testInt(5, 6);
try {
//Get the second value set in Expectations
mockedA.testInt(5, 6);
fail();
} catch (IllegalArgumentException ex) {
assertEquals("test", ex.getMessage());
}
}
@Test
public void testWhen6_2() {
ClassA mockedA = mock(ClassA.class);
when(mockedA.testInt(5, 6)).thenThrow(new IllegalArgumentException("test"));
try {
//Get the second value set in Expectations
mockedA.testInt(5, 6);
fail();
} catch (IllegalArgumentException ex) {
assertEquals("test", ex.getMessage());
}
}
Stubing by the Answer Interface (https://javadoc.io/static/org.mockito/mockito-core/3.1.0/org/mockito/stubbing/Answer.html) is allowed.
@Test
public void testWhen7() {
ClassA mockedA = mock(ClassA.class);
when(mockedA.testInt(5, 6)).thenAnswer(
new Answer<Integer>() {
public Integer answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
System.out.println("getArguments----------------");
for (Object arg : args) {
System.out.println(arg);
}
//Double the value of real processing
return (int)invocation.callRealMethod() * 2;
}
}
);
//Result specified by mock
assertEquals(22, mockedA.testInt(5, 6));
}
InvocationOnMock is available for the callback method. It is possible to get the arguments passed via InvocationOnMock, the mocked method, and execute the actual method.
It is possible to create a mock with one line with the following implementation.
@Test
public void testWhen8() {
ClassA mockedA = when(mock(ClassA.class).testInt(anyInt(), anyInt())).thenReturn(99).getMock();
assertEquals(99, mockedA.testInt(5, 6));
}
By using Spy, it is possible to mock only a part of the object. The sample below returns a mocked value only when executed with specific arguments. Other than that, the execution result of the actual method is returned.
@Test
public void testSpy1() {
ClassA objA = new ClassA();
ClassA spyA = Mockito.spy(objA);
when(spyA.testInt(5, 6)).thenReturn(99);
//Result specified by mock
assertEquals(99, spyA.testInt(5, 6));
//Unmocked results
//The real thing is called differently from the time of mock.
assertEquals(12, spyA.testInt(6, 6));
}
You can use verify to verify how the mocked method was executed.
@Test
public void testVerify1() {
ClassA mockedA = mock(ClassA.class);
mockedA.testInt(5,6);
mockedA.testInt(3,4);
//Make sure the following method is executed(Do not verify the order)
verify(mockedA).testInt(3,4);
verify(mockedA).testInt(5,6);
}
times/ [never](https://javadoc. io /static/org.mockito/mockito-core/3.1.0/org/mockito/Mockito.html#never--)/[atLeastOnce](https://javadoc.io/static/org.mockito/mockito-core /3.1.0/org/mockito/Mockito.html#atLeastOnce--)/[atMostOnce](https://javadoc.io/static/org.mockito/mockito-core/3.1.0/org/mockito/Mockito. html # atMostOnce--) / atLeast/[atMost] It is possible to verify the number of times it has been executed by (https://javadoc.io/static/org.mockito/mockito-core/3.1.0/org/mockito/Mockito.html#atMost-int-).
@Test
public void testVerify2() {
ClassA mockedA = mock(ClassA.class);
mockedA.testInt(5,6);
mockedA.testInt(3,4);
//2 times testInt(?,?)Is being executed
verify(mockedA, times(2)).testInt(anyInt(),anyInt());
// testInt(1,1)Make sure it is not running
verify(mockedA, never()).testInt(1,1);
}
By using [inOrder](https://javadoc.io/doc/org.mockito/mockito-core/3.1.0/org/mockito/Mockito.html#inOrder-java.lang.Object ...-) It is possible to verify the execution order of methods.
class T1 {
void t1(String a) {
}
}
class T2 {
void t2(String a) {
}
}
@Test
public void testVerify3() {
T1 mocked = mock(T1.class);
mocked.t1("first");
mocked.t1("second");
//Verify including order
InOrder order = inOrder(mocked);
order.verify(mocked).t1("first");
order.verify(mocked).t1("second");
}
@Test
public void testVerify4() {
//It is also possible to check the execution order of multiple objects
T1 t1 = mock(T1.class);
T2 t2 = mock(T2.class);
t1.t1("call 1");
t2.t2("call 2");
t1.t1("call 3");
t2.t2("call 4");
//Verify including order
InOrder order = inOrder(t1, t2);
order.verify(t1).t1("call 1");
order.verify(t2).t2("call 2");
order.verify(t1).t1("call 3");
order.verify(t2).t2("call 4");
}
The final method cannot be mocked with the default settings. To enable mocking of the final method, you need to create the following files.
/mockito-extensions/org.mockito.plugins.MockMaker
mock-maker-inline
class ClassB {
final public int testFinal(int x, int y) {
return x + y;
}
}
@Test
public void testFinal1() {
//final methods cannot be mocked by default
// /mockito-extensions/org.mockito.plugins.Create a MockMaker file and
//You need to enter the following characters
// mock-maker-inline
ClassB mockedB = mock(ClassB.class);
when(mockedB.testFinal(5, 6)).thenReturn(99);
//Result specified by mock
assertEquals(99, mockedB.testFinal(5, 6));
}
When using PowerMock, annotate the class as follows. ・ @RunWith (PowerMockRunner.class) ・ @PrepareForTest ({ZZZZ.class, XXX.class})
PrepareForTest should be a class that needs to be manipulated at the bytecode level during the test. When mocking static methods, constructors, and private methods.
package powermockTest;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest({Calendar.class, java.lang.Math.class})
public class Test002 {
}
In the example below, the private method test1 is mocked, and it is confirmed that the mocked value is returned when executed via test2.
class ClassPrivate {
private int test1(int x, int y) {
return x + y;
}
public int test2(int x) {
return test1(x, x) + 1;
}
}
@Test
public void testPrivate1() {
ClassPrivate objA = new ClassPrivate();
ClassPrivate spyA = PowerMockito.spy(objA);
PowerMockito.when(spyA.test1(5, 5)).thenReturn(99);
//Result specified by mock
assertEquals(100, spyA.test2(5));
assertEquals(99, spyA.test1(5,5));
}
The example below is a mocked example of java.lang.Math.random.
//An example of mocking random
@Test
public void testStatic1() {
//Please set the following in the class
// @PrepareForTest({ java.lang.Math.class})
PowerMockito.mockStatic(java.lang.Math.class);
PowerMockito.when(java.lang.Math.random()).thenReturn(1.5);
assertEquals(1.5, java.lang.Math.random(), 0.1);
}
If the Static method can be mocked, the current time can be disguised as a convenient time.
//Example of mocking the current time
@Test
public void testStatic2() {
//Please set the following in the class
// @PrepareForTest({ Calendar.class})
Calendar cal = Calendar.getInstance();
cal.set(2018, 1, 25, 23, 32, 30);
cal.set(Calendar.MILLISECOND, 0);
PowerMockito.mockStatic(Calendar.class);
PowerMockito.when(Calendar.getInstance()).thenReturn(cal);
Calendar c = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
assertEquals("20180225233230000", sdf.format(c.getTime()));
}
Use PowerMockito.whenNew to mock the instance created in the method.
class classA {
public String getA() {
return "getA";
}
}
class classB {
public String hoge(String x) {
return x + (new classA()).getA();
}
}
//Mock the instance to mock the object used in the method under test
@Test
public void testInner() throws Exception {
classA mockedA = Mockito.mock(classA.class);
when(mockedA.getA()).thenReturn("abc");
PowerMockito.whenNew(classA.class).withNoArguments().thenReturn(mockedA);
classB obj = new classB();
assertEquals("testabc", obj.hoge("test"));
}
I think that there are many use cases and the low learning cost has an advantage over JMockit. In addition, it seems that the coverage measurement that existed in JMockit cannot be performed.
Recommended Posts