[JAVA] Try using powermock-mockito2-2.0.2

Introduction

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 and powermock

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/

Try using

Use Mockito

Use stubs

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));
	}
}

Argument matcher

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));
	}

Range of influence of mocking

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));

	}

How to return different results with the same argument value

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);

How to output an exception with a mocked method

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());
		}
	}

Callback stub

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.

Create a mock with one line

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));
	}

Partial mock using Spy

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));

	}

Verification using Verify

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);
	}

Verification of the number of executions

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);
	}

Verification of execution order

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");
	}

Mocking final methods

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

image.png

	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));

	}

Use PowerMock

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 {
}

Mocking Private methods

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));

	}

Mocking Static methods

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);
	}

Impersonation of current time

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()));
	}

Mock the instance created in the method

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"));

	}

Summary

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

Try using powermock-mockito2-2.0.2
Try using libGDX
Try using Maven
Try using GraalVM
Try using jmockit 1.48
Try using sql-migrate
Try using SwiftLint
Try using Log4j 2.0
Try using Axon Framework
Try using JobScheduler's REST-API
Try using java.lang.Math methods
Try using PowerMock's WhiteBox
Try using Talend Part 2
Try using Talend Part 1
Try using F # list
Try using each_with_index method
Try using Spring JDBC
Try using GloVe with Deeplearning4j
Try using view_component with rails
Try scraping using java [Notes]
Try using Cocoa from Ruby
Try using letter_opener_web for inquiries
[Swift] Try using Collection View
Try using IntelliJ IDEA once
Try using Spring Boot Security
Try using gRPC in Ruby
[Rails] Try using Faraday middleware
[Processing] Try using GT Force.
[Programming Encyclopedia] §2 Try using Ruby
People using docker Try using docker-compose
Try using Redmine on Mac docker
Try using Redis with Java (jar)
[Java] Try to implement using generics
Try using the messaging system Pulsar
Try using IBM Java method tracing
Try using Hyperledger Iroha's Java SDK
[Java] Where did you try using java?
Try using Java framework Nablarch [Web application]
Try using || instead of the ternary operator
Try HiveRunner
Try using the service on Android Oreo
Try using the Stream API in Java
Try using the Rails API (zip code)
Try Mockito
Study Java Try using Scanner or Map
Try Selenium
Try using JSON format API in Java
Try DbUnit
Try using Spring Boot with VS Code
Try using Reladomo's MT Loader (Multi-Threaded Matcher Loader)
try docker-compose
Try using JobScheduler's REST-API --Java RestClient implementation--
Try using Kong + Konga with Docker Compose.
Try using the Emotion API from Android
Try using the Wii remote with Java
Try Lombok
Try using simple_form / edit even child models
Try implementing GraphQL server using grahpql-java-tools (+ kotlin)
Try using Firebase Cloud Functions on Android (Java)
Try using JobScheduler's REST-API --Java RestClient Test class-
Try using conditional branching other than if statement