[JAVA] Try using jmockit 1.48

Purpose

The purpose of this article is a memo when investigating jmockit 1.48.

document http://jmockit.github.io/index.html

Source code https://github.com/jmockit/jmockit1

JavaDoc https://repo1.maven.org/maven2/org/jmockit/jmockit/1.48/jmockit-1.48-javadoc.jar

** 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

What is jmockit

When unit testing with xUnit, the dependent parts can be a problem and difficult to test. image.png

For example, in the following cases. -When it is difficult to return arbitrary contents to the test target due to dependent parts

In these cases, unit testing is facilitated by using methods created with jmockit instead of dependent parts.

image.png

By using jmockit, it is possible to pass a value convenient for testing to the test target instead of the dependent part, and to record and verify how the dependent part was called from the test target. I will.

Easy to use

(1) Download Jar from the following and refer to it from the project. https://mvnrepository.com/artifact/org.jmockit/jmockit

Or for Maven, add the following to pom.xml

<!-- https://mvnrepository.com/artifact/org.jmockit/jmockit -->
<dependency>
    <groupId>org.jmockit</groupId>
    <artifactId>jmockit</artifactId>
    <version>1.48</version>
    <scope>test</scope>
</dependency>

(2) Add a JUnit test case

package jmockittest;

import static org.junit.Assert.*;

import org.junit.Test;

import mockit.Mock;
import mockit.MockUp;

public class SimpleTest {

	@Test
	public void test() {
        new MockUp<java.lang.Math>() {
        	@Mock
        	public double random() {
        		//Always 2.Returns 5 random()Method
        		return 2.5;
        	}
        };
		assertEquals(2.5, Math.random(), 0.1);
		assertEquals(2.5, Math.random(), 0.1);
	}

}

(3) In the execution configuration at the time of junit execution, add "-javaagent: jmockit-1.48.jar" to the VM argument and execute. image.png

image.png

See below for details on how to do this: http://jmockit.github.io/tutorial/Introduction.html#runningTests

troubleshoot

If initializationError occurs

** Event ** image.png

** Error tracing **

java.lang.Exception: Method testVerifications should have no parameters
	at org.junit.runners.model.FrameworkMethod.validatePublicVoidNoArg(FrameworkMethod.java:76)
	at org.junit.runners.ParentRunner.validatePublicVoidNoArgMethods(ParentRunner.java:155)
//Abbreviation
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)

** Cause ** "-Javaagent: jmockit-1.48.jar" is not added to the VM argument.

Is the order of classpaths important? Or do I need to use RunWith?

Occasionally there is a statement that it is important to do the build classpath in the order jmockit → junit. https://stackoverflow.com/questions/32817982/jmockit-wasnt-properly-initialized?rq=1

Probably this is not a problem in the current version, or it is because the VM argument is not given "-javaagent: jmockit-X.XX.jar".

Also, there seems to be a way to use "@ RunWith (JMockit.class)" as another solution to the order of the build classpath, but at least as of 1.48, this attribute does not exist.

https://github.com/jmockit/jmockit1/issues/554

How to use jmockit

Mocking Mocking provides a mechanism to separate the class under test from (part of) its dependencies. Use the @ Mocked / @ Injectable / @ Capturing annotations to create a mocked instance. Mocked instances can be set to the expected behavior in Expectations (#expectations) and can be verified in Verifications (#verifications) how the mocked instance was executed.

@Mocked annotation

It is possible to mock using the @Mocked annotation as a test case method parameter or test case class field. If you use the @Mocked annotation, all instances of the same type will be mocked for the duration of the test using it.

Any type can be mocked except primitive type and array type.

Now let's consider how to use the following class as a mock object.

** Test target **

package SampleProject;

public class Hoge001 {
	public int hoge(int x, int y) {
		return x + y;
	}
	public String hoge(String x) {
		return "test" + x;
	}
}

** Example of using Mocked **

package jmockittest;

import static org.junit.Assert.*;

import org.junit.Test;

import SampleProject.Hoge001;
import mockit.Expectations;
import mockit.Mocked;

public class Test001 {
	//When not using a mock...
	@Test
	public void test0() {
		Hoge001 hoge = new Hoge001();
		assertEquals(11, hoge.hoge(5,6));
		assertEquals("testxxx", hoge.hoge("xxx"));
	}

	//You can create a mocked instance by specifying it as a parameter of the test method
	@Test
	public void test1(@Mocked Hoge001 mock) {
		new Expectations() {{
			//hoge is x=5, y =If called by 6, return 99 the first time
			mock.hoge(5,6);
			result  = 99;
		}};
		//If you specify the result of the method in Expectations, the value is fetched.
		assertEquals(99, mock.hoge(5,6));
		//If the method result is not specified in Expectations, the initial value(null)Becomes
		assertEquals(null, mock.hoge("xxx"));

		// @If you use Mocked, all applicable instances will be mocked for the duration of the test.
		Hoge001 hoge = new Hoge001();
		assertEquals(99, hoge.hoge(5,6));
		assertEquals(null, hoge.hoge("xxx"));

	}
}

test0 () is a test case when not mocked, and test1 () is a test case using @Mocked as a parameter. During the test1 () test case, all Hoge001 instances are mocked instances.

Confirm that it will be a mock even if it is not created directly in the test case

The following test confirms that it will be mocked even if you have not created the instance directly in the test case.

** Test target ** The test target is Hoge002, which is used by creating an instance of Hoge001.

package SampleProject;

public class Hoge002 {
	public int test(int x , int y) {
		Hoge001 hoge1 = new Hoge001();
		return hoge1.hoge(x*2, y*2);
	}
}

** Test code **

package jmockittest;

import static org.junit.Assert.*;

import org.junit.Test;

import SampleProject.Hoge001;
import SampleProject.Hoge002;
import mockit.Expectations;
import mockit.Mocked;

public class Test001_3 {

	//You can create a mocked instance by specifying it as a parameter of the test method
	@Test
	public void test1(@Mocked Hoge001 mock) {
		new Expectations() {{
			//hoge is x=10, y =If called by 12, the first time returns 99
			mock.hoge(10,12);
			result  = 99;
		}};
		Hoge002 hoge2 = new Hoge002();
		assertEquals(99, hoge2.test(5,6));
	}
}

If you run this test case, you can see that the Hoge001 created by Hoge002 is a mocked version.

Case using @Mocked for class field

If you use @Mocked for a class field, all tests for the class will mock the target class.

** Test code **

package jmockittest;

import static org.junit.Assert.*;

import org.junit.Test;

import SampleProject.Hoge001;
import SampleProject.Hoge002;
import mockit.Expectations;
import mockit.Mocked;

public class Test001_2 {

	//Mocked instances are available in each test case by specifying them as test class fields
	@Mocked
	private Hoge001 fieldMocked;
	@Test
	public void test1() {
		new Expectations() {{
			fieldMocked.hoge(anyInt, anyInt);
			result = 100;
		}};
		assertEquals(100, fieldMocked.hoge(1,2));
	}
	@Test
	public void test2() {
		new Expectations() {{
			//hoge is x=10, y =If called by 12, the first time returns 99
			fieldMocked.hoge(10,12);
			result  = 99;
		}};
		Hoge002 hoge2 = new Hoge002();
		assertEquals(99, hoge2.test(5,6));
	}
}

Cascaded mock

Suppose you have functionality provided using many different objects. For example, it is not uncommon to have a call like "obj1.getObj2 (...). GetYetAnotherObj (). DoSomething (...)". Let's look at an example of a mock in this case.

In the example below, the code is to check if the method that returns an object such as mock.getDepend1 (). Output () is mocked.

** Class under test **

package SampleProject;

public class Depend001 {
	private String prefix;
	public Depend001(String p) {
		this.prefix = p;
	}
	public String output(String msg) {
		return this.prefix + msg;
	}
}
package SampleProject;

public class Hoge003 {
	private Depend001 d1;
	public Depend001 d2;
	public Hoge003() {

	}

	public Hoge003(Depend001 depend1, Depend001 depend2) {
		this.d1 = depend1;
		this.d2 = depend2;
	}

	public String output() {
		String ret = "";
		ret = ret + this.d1.output("test1") + "\n";
		ret = ret + this.d2.output("test2") + "\n";
		return ret;
	}
	public Depend001 getDepend1() {
		return this.d1;
	}
}

** Test code **

package jmockittest;

import static org.junit.Assert.*;

import org.junit.Test;

import SampleProject.Hoge003;
import mockit.Expectations;
import mockit.Mocked;

public class Test002 {
	@Test
	public void test1(@Mocked Hoge003 mock) {
		new Expectations() {{
			mock.getDepend1().output(anyString);
			result  = "abcde";
		}};
		assertEquals("abcde", mock.getDepend1().output("abc"));
	}

}

It was confirmed that the expected behavior of the target method can be changed by mocking the original Hoge003 class without explicitly mocking Depend001 of the branches and leaves as in the above sample.

@Injectable annotation

It is an annotation for mocking like the @Mocked annotation, but the difference from the @Mocked annotation is that the mock is limited to one instance. It can also be used in combination with the @Tested annotation for automatic injection into the object under test.

Difference from @Mocked annotation

To check the difference between @Mocked annotation and @Injectable annotation, change the test code used in @Mocked annotation to @Injectable and check.

package jmockittest;

import static org.junit.Assert.*;

import org.junit.Test;

import SampleProject.Hoge001;
import mockit.Expectations;
import mockit.Injectable;

public class Test004 {

	//You can create a mocked instance by specifying it as a parameter of the test method
	@Test
	public void test1(@Injectable Hoge001 mock) {
		new Expectations() {{
			//hoge is x=5, y =If called by 6, return 99 the first time
			mock.hoge(5,6);
			result  = 99;
		}};
		//If you specify the result of the method in Expectations, the value is fetched.
		assertEquals(99, mock.hoge(5,6));
		//If the method result is not specified in Expectations, the initial value(null)Becomes
		assertEquals(null, mock.hoge("xxx"));

		// @Not all relevant instances are mocked, as is the case with Mocked.
		Hoge001 hoge = new Hoge001();
		assertEquals(11, hoge.hoge(5,6));
		assertEquals("testxxx", hoge.hoge("xxx"));

	}
}

When using the @Mocked annotation, it was mocked every time an instance of the target class was created during the test period, but by using @Injectable, the number of instances that can be mocked is limited to one. You can see that you are doing it.

Injection for @Tested annotation

Let's check the sample that injects the mock into the object to be tested specified by the @Tested annotation. There are two methods, one is to inject into the constructor argument of the object to be tested specified by @Tested, and the other is to inject into the field to be tested.

** How to inject into constructor arguments ** The following is an example of specifying depend1 and depend2 which are the arguments of the constructor of Hoge003 (Depend001 depend1, Depend001 depend2).

package jmockittest;

import static org.junit.Assert.*;

import org.junit.Test;

import SampleProject.Depend001;
import SampleProject.Hoge003;
import mockit.Expectations;
import mockit.Injectable;
import mockit.Tested;

public class Test003 {
	@Tested
	Hoge003 target;

	@Injectable
	Depend001 depend1;

	@Injectable
	Depend001 depend2;

	@Test
	public void test1() {
		new Expectations() {{
			depend1.output(anyString);
			result  = "abcde";
			depend2.output(anyString);
			result  = "xxxxx";
		}};
		assertEquals("abcde\nxxxxx\n", target.output());
	}

}

** How to inject into the field ** Below is a sample to inject into the d1 and d2 fields of the Hoge003 object.

package jmockittest;

import static org.junit.Assert.*;

import org.junit.Test;

import SampleProject.Depend001;
import SampleProject.Hoge003;
import mockit.Expectations;
import mockit.Injectable;
import mockit.Tested;

public class Test003B {
	@Tested
	Hoge003 target;

	@Injectable
	Depend001 d1;

	@Injectable
	Depend001 d2;

	@Test
	public void test1() {
		new Expectations() {{
			d1.output(anyString);
			result  = "abcde";
			d2.output(anyString);
			result  = "xxxxx";
		}};
		assertEquals("abcde\nxxxxx\n", target.output());
	}
}
How to inject into a field or construct of a primitive type

By using the value element of the @Injectable annotation, it is possible to inject into the field or construct of the primitive type to be tested specified by the @Tested annotation.

package jmockittest;

import org.junit.Test;

import SampleProject.Depend001;
import mockit.Injectable;
import mockit.Tested;

public class Test005 {
	@Tested
	Depend001 tested;

	@Test
	public void test1(@Injectable("abc") String p) {
		//Output the following
		// abcaaa
		System.out.println(tested.output("aaa"));
	}
	@Test
	public void test2(@Injectable("abc") String prefix) {
		//Output the following
		// abcbbb
		System.out.println(tested.output("bbb"));
	}
}

test1 is injected by specifying the constructor argument p of the object to be tested, and test2 is injected by specifying the field prefix of the object to be tested.

Optional element of @Tested annotation
type name Optional elements and description Specified value
boolean availableDuringSetup The tested class is the test setup method (that is,@Before or@Indicates whether the method annotated as BeforeMethod) is instantiated and initialized before execution, or after them. false
boolean fullyInitialized Indicates that a value should be assigned to each non-final field of the tested object that is eligible for injection. If you are using Spring, the usage is described on the next page. https://stackoverflow.com/questions/25856210/injecting-only-some-properties-mocking-others false
boolean global Creates a single named instance of the class under test to indicate whether it will be used throughout the test run. false
String value Field to test/If the parameter type is string, primitive or wrapper type, numeric type, or enumeration type, specify a literal value. ""

** Test code to validate availableDuringSetup and global **

package jmockittest;

import org.junit.Before;
import org.junit.Test;

import SampleProject.Hoge001;
import mockit.Tested;

public class Test007 {
	@Tested(availableDuringSetup=true, global=true)
	Hoge001 tested;

	@Before
	public void before()
	{
		//Other than null: If availableDuringSetup is false, it will be null.
		System.out.println("before:" + tested);
	}

	@Test
	public void test1() {
		//Non-null
		System.out.println("test1:" + tested);
	}
	@Test
	public void test2() {
		//You can see that the same object as test1 is used except null
		System.out.println("test2:" + tested);
	}
}

@Capturing annotation

It is possible to mock the default class and interface by using the @Capturing annotation. The sample below is a sample that creates a mocked method for an interface rather than an individual implementation class.

package jmockittest;

import static org.junit.Assert.*;

import org.junit.Test;

import mockit.Capturing;
import mockit.Expectations;

public class Test006 {
	public interface Service { int doSomething(); }
	final class ServiceImpl implements Service { public int doSomething() { return 1; } }

	public final class TestedUnit {
	   private final Service service1 = new ServiceImpl();
	   private final Service service2 = new Service() { public int doSomething() { return 2; } };

	   public int businessOperation() {
	      return service1.doSomething() + service2.doSomething();
	   }
	}

	//Create a mock for an interface or default class
	@Test
	public void test1(@Capturing Service anyService) {
	      new Expectations() {{ anyService.doSomething(); returns(3, 4); }};

	      int result = new TestedUnit().businessOperation();

	      assertEquals(7, result);
	}
}

Expectations Expectations sets the expected behavior for mock objects associated with a particular test.

Set the expected value

During Expectations, you can specify which parameters should be specified for the mock object method and which value should be returned. In the example below, it is an example of setting what value is returned when the "String hoge (String)" and "int hoge (int, int)" methods are executed.

package jmockittest;

import static org.junit.Assert.*;

import org.junit.Test;

import SampleProject.Hoge001;
import mockit.Delegate;
import mockit.Expectations;
import mockit.Mocked;

public class Test008 {

	//If you specify the result of the method in Expectations, make sure that the value is obtained.
	@Test
	public void test1(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge("test");
			result = "abcde";

			mock.hoge(5,6);
			result  = 99;
			result = 100;
			result = 101;

		}};
		// mock.hoge("test")Get the expected value when executing
		assertEquals("abcde", mock.hoge("test"));


		// mock.hoge(5,6)Get the expected value when executing
		//Get the first value set in Expectations
		assertEquals(99, mock.hoge(5,6));
		//Get the second value set in Expectations
		assertEquals(100, mock.hoge(5,6));
		//Get the third value set in Expectations
		assertEquals(101, mock.hoge(5,6));
		//Get the last value set in Expectations
		assertEquals(101, mock.hoge(5,6));
		//Get the last value set in Expectations
		assertEquals(101, mock.hoge(5,6));

		//If the arguments are different, it will be the initial value
		assertEquals(0, mock.hoge(7,6));
	}
}
Example described in returns

Multiple results can be listed together in returns as shown below.

		new Expectations() {{
			mock.hoge("test");
			result = "abcde";

			mock.hoge(5,6);
			returns(99, 100, 101);

		}};

Flexible way to specify arguments

In the previous example, the return value was returned only when the value of a specific argument was accepted, but the argument value can be set flexibly by specifying any ~ or with ~ as the argument.

Use of any field

Expectations has several any fields that represent arbitrary ambitions.

type name
Object any
Boolean anyBoolean
Byte anyByte
Character anyChar
Double anyDouble
Float anyFloat
Integer anyInt
Long anyLong
Short anyShort
String anyString

An example using the any field is as follows.

	@Test
	public void test1_1(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge(anyString);
			result = "abcde";

			mock.hoge(anyInt, anyInt);
			result  = 99;

		}};
		// mock.hoge("test")Get the expected value when executing
		assertEquals("abcde", mock.hoge("test"));
		assertEquals("abcde", mock.hoge("hogehoget"));


		// mock.hoge(5,6)Get the expected value when executing
		assertEquals(99, mock.hoge(5,6));
		assertEquals(99, mock.hoge(99,1234));
	}
Combine fixed argument values with arbitrary argument values

You can also combine fixed argument values with arbitrary argument values. In the example below, create a mock method that returns 10 for hoge (5,6) and 99 otherwise.

	@Test
	public void test1_2(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge(5,6);
			result = 10;

			mock.hoge(anyInt, anyInt);
			result  = 99;

		}};

		// mock.hoge(5,6)Get the expected value when executing
		assertEquals(10, mock.hoge(5,6));
		assertEquals(99, mock.hoge(99,1234));
	}

When combining with the value of any argument, enter the fixed value first.

Use of with method

By using the with-method, it is possible to flexibly judge whether or not the mock method specified in Expectations matches.

Method Description
with(Delegate<? super T> objectWithDelegateMethod) Use the delegate method to determine if the arguments match. If the return value of the delegate method is true, it means a match.
withEqual(T arg) Checks if the specified value matches the argument at mock execution. In general, do not use this method, but use the method of passing the value of the desired argument.
withEqual(double value, double delta) Matches if close to the value specified by delta
withEqual(float value, double delta) Matches if close to the value specified by delta
withAny(T arg) Consider using anyBoolean, anyByte, anyChar, anyDouble, anyFloat, anyInt, anyLong, anyShort, anyString, any.
withNotEqual(T arg) If the value is other than the specified value, it is considered to match.
withNotNull() If the specified value is non-null, it is considered a match
withNull() If the specified value is NULL, it is assumed to match
withInstanceOf(Class argClass) Make sure it is an instance of the specified class.
withInstanceLike(T object) Make sure it is an instance of the same class as the specified object. withInstanceOf(object.getClass())Will be equivalent to
withSameInstance(T object) Make sure they are exactly the same instance
withPrefix(T text) If it contains certain characters, it is considered a match
withSubstring(T text) If the beginning matches the specified character, it is considered to match.
withSuffix(T text) If the end matches the specified character, it is considered to match.
withMatch(T regex) You can specify whether the regular expression matches
Example of using delegate method with with

By using the delegate method with with, it is possible to judge whether the mock argument matches with the method.

	@Test
	public void test1_4(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge(with(new Delegate<Integer>() {
				@Mock boolean validate(int value) {
					return value >= 0;
				}
			}),anyInt);
			result = 99;
		}};


		//Since x is positive, it matches the value set in the mock
		assertEquals(99, mock.hoge(1,2));
		//Since x is negative, it does not match the value set in the mock
		assertEquals(0, mock.hoge(-1,2));
	}
Example using withEqual

Basically it is better to use literals as they are than to use withEqual. However, if you want to use floating point, you should use withEqual.

	class testWithEqual {
		int test1(double v) {
			return 1000;
		}
		int test2(int v) {
			return 2000;
		}
	}

	@Test
	public void test_withEqual1(@Mocked testWithEqual mock) {
		new Expectations() {{
			mock.test2(withEqual(100));
			result = 99;
		}};
		//Matching mock.test2(100)Same as
		assertEquals(99, mock.test2(100));
		//It does not match
		assertEquals(0, mock.test2(101));
	}
	@Test
	public void test_withEqual2(@Mocked testWithEqual mock) {
		new Expectations() {{
			mock.test1(withEqual(100, 1));
			result = 99;
		}};
		//Match
		assertEquals(99, mock.test1(100.0));
		assertEquals(99, mock.test1(101.0));
		assertEquals(99, mock.test1(99.0));
		//It does not match
		assertEquals(0, mock.test1(101.1));
		assertEquals(0, mock.test1(98.99));
	}

Examples of withInstanceOf, withInstanceOf, withSameInstance

You can use withInstanceOf, withInstanceOf, withSameInstance to see if it matches a particular instance.

	class classA {
	}
	class classB {
	}
	class classX  {
		public int method1(Object obj) {
			return 999;
		}
	}

	@Test
	public void test_withInst1(@Mocked classX mock) {
		new Expectations() {{
			mock.method1(withInstanceOf(classA.class));
			result = 99;
		}};
		//Match
		{
			classA obj = new classA();
			assertEquals(99, mock.method1(obj));
		}

		//It does not match
		{
			classB obj = new classB();
			assertEquals(0, mock.method1(obj));
		}
	}

	@Test
	public void test_withInst2(@Mocked classX mock) {
		new Expectations() {{
			classA objA = new classA();
			mock.method1(withInstanceLike(objA));
			result = 99;
		}};
		//Match
		{
			classA obj = new classA();
			assertEquals(99, mock.method1(obj));
		}

		//It does not match
		{
			classB obj = new classB();
			assertEquals(0, mock.method1(obj));
		}
	}
	@Test
	public void test_withInst3(@Mocked classX mock) {
		classA obj1 = new classA();
		new Expectations() {{
			mock.method1(withSameInstance(obj1));
			result = 99;
		}};
		//Match
		{
			assertEquals(99, mock.method1(obj1));
		}

		//It does not match
		{
			classA obj2 = new classA();
			assertEquals(0, mock.method1(obj2));
		}
	}
Examples of withPrefix, withSubstring, withSuffix, withMatch

By using withPrefix, withSubstring, withSuffix, withMatch, it is possible to check whether a part of the string matches.

	@Test
	public void test_withString1(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge(withPrefix("abc"));
			result = "test";
		}};

		//The following match
		assertEquals("test", mock.hoge("abc"));
		assertEquals("test", mock.hoge("abcAA"));

		//The following do not match
		assertEquals(null, mock.hoge("AAabc"));
		assertEquals(null, mock.hoge("AabcA"));
		assertEquals(null, mock.hoge("xx"));
	}

	@Test
	public void test_withString2(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge(withSuffix("abc"));
			result = "test";
		}};

		//The following match
		assertEquals("test", mock.hoge("abc"));
		assertEquals("test", mock.hoge("AAabc"));

		//The following do not match
		assertEquals(null, mock.hoge("abcAA"));
		assertEquals(null, mock.hoge("AabcA"));
		assertEquals(null, mock.hoge("xx"));
	}
	@Test
	public void test_withString3(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge(withSubstring("abc"));
			result = "test";
		}};

		//The following match
		assertEquals("test", mock.hoge("abc"));
		assertEquals("test", mock.hoge("abcAA"));
		assertEquals("test", mock.hoge("AAabc"));
		assertEquals("test", mock.hoge("AabcA"));

		//The following do not match
		assertEquals(null, mock.hoge("xx"));
	}
	@Test
	public void test_withString4(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge(withMatch("[0-9]+"));
			result = "test";
		}};

		//The following match
		assertEquals("test", mock.hoge("1234"));

		//The following do not match
		assertEquals(null, mock.hoge("xxx"));
	}

How to separate mock methods depending on how the instance is created

It is possible to divide the mock method according to how the instance is created in Expectations. The following example shows a sample that applies the mock method only to the instance created by executing "new TestA (10)".

	class TestA {
		public TestA(int x) {

		}
		public int hoge() {
			return 99999;
		}
	}

	@Test
	public void test8(@Mocked TestA mock) {
		new Expectations() {{
			TestA t1 = new TestA(10);
			t1.hoge();
			result = 10;

		}};

		{
			TestA obj = new TestA(10);
			assertEquals(10, obj.hoge());
		}
		{
			TestA obj = new TestA(99);
			assertEquals(0, obj.hoge());
		}
	}

How to raise an exception

You can raise an exception while processing a mock method. The following example raises an IllegalArgumentException while executing the hoge () method.

	//An example of returning a method exception in Expectations
	@Test
	public void test2(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge(5,6);
			result = 99;
			result = new IllegalArgumentException("test");
		}};
		//Get the first value set in Expectations
		assertEquals(99, mock.hoge(5,6));
		try {
			//Get the second value set in Expectations
			mock.hoge(5,6);
			fail();

		} catch (IllegalArgumentException ex) {
			assertEquals("test", ex.getMessage());
		}
	}

Check the number of executions

It is possible to specify the number of method executions by specifying times, maxTImes, minTimes in Expectations.

Field Description
tiems Specifies how many times the method is called during execution. If it is called a different number of times, an error will occur.
maxTimes Specifies the maximum number of methods to be called. If it is called more times than this, an error will occur.
minTimes Specifies the minimum number of methods to be called. If it is called less than this number, an error will occur.
	@Test
	public void test4_1(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge(anyInt, anyInt);
			result  = 99;
			times = 3;

		}};
		assertEquals(99, mock.hoge(5,6));
		assertEquals(99, mock.hoge(99,1234));
		assertEquals(99, mock.hoge(3,6));
	}
	//This test fails with Missing 2 invocations
	@Test
	public void test4_2(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge(anyInt, anyInt);
			result  = 99;
			times = 3;

		}};
		assertEquals(99, mock.hoge(3,6));
	}

	//This test results in an Unexpected invocation and an error
	@Test
	public void test4_3(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge(anyInt, anyInt);
			result  = 99;
			times = 3;

		}};
		assertEquals(99, mock.hoge(5,6));
		assertEquals(99, mock.hoge(99,1234));
		assertEquals(99, mock.hoge(3,6));
		assertEquals(99, mock.hoge(3,6));
	}

	@Test
	public void test5_1(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge(anyInt, anyInt);
			result  = 99;
			minTimes = 3;

		}};
		assertEquals(99, mock.hoge(5,6));
		assertEquals(99, mock.hoge(99,1234));
		assertEquals(99, mock.hoge(3,6));
	}

	//This test fails with Missing 2 invocations
	@Test
	public void test5_2(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge(anyInt, anyInt);
			result  = 99;
			minTimes = 3;

		}};
		assertEquals(99, mock.hoge(3,6));
	}
	@Test
	public void test5_3(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge(anyInt, anyInt);
			result  = 99;
			minTimes = 3;

		}};
		assertEquals(99, mock.hoge(5,6));
		assertEquals(99, mock.hoge(99,1234));
		assertEquals(99, mock.hoge(3,6));
		assertEquals(99, mock.hoge(3,6));
	}
	@Test
	public void test6_1(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge(anyInt, anyInt);
			result  = 99;
			maxTimes = 3;

		}};
		assertEquals(99, mock.hoge(5,6));
		assertEquals(99, mock.hoge(99,1234));
		assertEquals(99, mock.hoge(3,6));
	}
	@Test
	public void test6_2(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge(anyInt, anyInt);
			result  = 99;
			maxTimes = 3;

		}};
		assertEquals(99, mock.hoge(3,6));
	}

	//This test results in an Unexpected invocation and an error
	@Test
	public void test6_3(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge(anyInt, anyInt);
			result  = 99;
			maxTimes = 3;

		}};
		assertEquals(99, mock.hoge(5,6));
		assertEquals(99, mock.hoge(99,1234));
		assertEquals(99, mock.hoge(3,6));
		assertEquals(99, mock.hoge(3,6));
	}

Custom specification of result using Delegate

Use Deglegate if you want to change the result returned by the mock based on the arguments when executing the mock method. In the example below, we are creating a mock method that returns a value that is double the input argument.

	@Test
	public void test7(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge(anyInt,anyInt);
			result= new Delegate<Integer>() {
				@SuppressWarnings("unused")
				int aDelegateMethod(int x, int y) {
					return x * 2 + y * 2;

		        }
		    };
		}};
		//Get the first value set in Expectations
		assertEquals(22, mock.hoge(5,6));
	}

Use of Invocation

It is possible to use Invocation as the first parameter of the Delegate method. Invocation offers the following getters:

Method Description
getInvocationCount() Number of calls
getInvocationIndex() Get current call Index
getInvokedArguments() Get the arguments used for the call
getInvokedInstance() Instance of the current call. Null for static methods
getInvokedMember() Call method/Get the constructor
proceed(Object... replacementArguments) Actual method/Execute the constructor
	@Test
	public void testDelegate2(@Mocked Hoge001 mock) {
		new Expectations() {{
			mock.hoge(anyInt,anyInt);
			result= new Delegate<Integer>() {
				@SuppressWarnings("unused")
				int aDelegateMethod(Invocation inv ,int x, int y) {
					System.out.println("--------------------------------");
					//Number of calls
					System.out.format("Invocation getInvocationCount %d \n", inv.getInvocationCount());
					//Index of current call
					System.out.format("Invocation getInvocationIndex() %d \n", inv.getInvocationIndex());
					//Get arguments
					System.out.println("getInvokedArguments");
					for(Object obj : inv.getInvokedArguments()) {
						System.out.println(obj);
					}
					//Get an instance
					System.out.format("Invocation getInvokedInstance() %s \n", inv.getInvokedInstance().toString());
					//Get the actual method
					System.out.format("Invocation getInvokedMember() %s \n", inv.getInvokedMember().toString());
					//The actual method can be executed.
					System.out.format("Invocation  proceed %s \n", inv.proceed().toString());
					//The actual method can be executed by tampering with the argument
					System.out.format("Invocation  proceed %s \n", inv.proceed(5,6).toString());
					return 0;
		        }
		    };
		}};
		//Get the first value set in Expectations
		Hoge001 a = new Hoge001();
		Hoge001 b = new Hoge001();
		a.hoge(5,6);
		a.hoge(45,63);
		b.hoge(99,100);
	}

The console log that executed the above is as follows.

--------------------------------
Invocation getInvocationCount 1 
Invocation getInvocationIndex() 0 
getInvokedArguments
5
6
Invocation getInvokedInstance() SampleProject.Hoge001@2a2d45ba 
Invocation getInvokedMember() public int SampleProject.Hoge001.hoge(int,int) 
Invocation  proceed 11 
Invocation  proceed 11 
--------------------------------
Invocation getInvocationCount 2 
Invocation getInvocationIndex() 1 
getInvokedArguments
45
63
Invocation getInvokedInstance() SampleProject.Hoge001@2a2d45ba 
Invocation getInvokedMember() public int SampleProject.Hoge001.hoge(int,int) 
Invocation  proceed 108 
Invocation  proceed 11 
--------------------------------
Invocation getInvocationCount 3 
Invocation getInvocationIndex() 2 
getInvokedArguments
99
100
Invocation getInvokedInstance() SampleProject.Hoge001@675d3402 
Invocation getInvokedMember() public int SampleProject.Hoge001.hoge(int,int) 
Invocation  proceed 199 
Invocation  proceed 11 

Mock part of an object

To mock only some of the methods instead of all, pass the object to Expectations as follows:

	@Test
	public void test10() {
		Hoge001 hoge = new Hoge001();
		new Expectations(hoge) {{
			hoge.hoge(5,6);
			result = 99;
		}};
		//Returns the result of the mock
		assertEquals(99, hoge.hoge(5,6));
		
		//Execute the actual method
		assertEquals(3, hoge.hoge(1,2));
		assertEquals("testabc", hoge.hoge("abc"));

	}

Verifications You can use Verifications, VerificationsInOrder, and FullVerifications to explicitly verify how a mock object was called.

	@Test
	public void test_v1(@Mocked Hoge001 mock) {
		mock.hoge(1,2);
		mock.hoge(2,3);
		mock.hoge(4,5);

		//
		new Verifications() {{
			mock.hoge(anyInt,anyInt);
			times = 3;
			mock.hoge(anyString);
			times = 0;
		}};
		//Verifications considers out-of-order or extra calls to pass
		new Verifications() {{
			mock.hoge(4,5);
			mock.hoge(1,2);
		}};
	}
	@Test
	public void test_v2(@Mocked Hoge001 mock) {
		mock.hoge(1,2);
		mock.hoge(2,3);
		mock.hoge(4,5);

		//VerificationsInOrder will result in an error if the order is different
		/*
		new VerificationsInOrder() {{
			mock.hoge(4,5);
			mock.hoge(1,2);
		}};
		*/
		new VerificationsInOrder() {{
			mock.hoge(1,2);
			mock.hoge(4,5);
		}};
	}
	@Test
	public void test_v3(@Mocked Hoge001 mock) {
		mock.hoge(1,2);
		mock.hoge(2,3);
		mock.hoge(4,5);

		//FullVerifications will result in an error if extra calls are made
		/*
		new FullVerifications() {{
			mock.hoge(1,2);
			mock.hoge(4,5);
		}};
		*/
		new FullVerifications() {{
			mock.hoge(1,2);
			mock.hoge(2,3);
			mock.hoge(4,5);
		}};
		//It will pass even if the order is different
		new FullVerifications() {{
			mock.hoge(4,5);
			mock.hoge(2,3);
			mock.hoge(1,2);
		}};
	}
Verification example using withCapture

You can get the instance given what parameter with withCapture in List.

	//Example of checking parameters with withCapture
	@Test
	public void test_v4(@Mocked Hoge001 mock) {
		mock.hoge(1,2);
		mock.hoge(2,3);
		mock.hoge(4,5);

		//
		new Verifications() {{
			List<Integer> argXList = new ArrayList<Integer>();
			List<Integer> argYList = new ArrayList<Integer>();
			mock.hoge(withCapture(argXList),withCapture(argYList));
			assertEquals(3, argXList.size());
			assertEquals(3, argYList.size());

			assertEquals(1, (int)argXList.get(0));
			assertEquals(2, (int)argXList.get(1));
			assertEquals(4, (int)argXList.get(2));

			assertEquals(2, (int)argYList.get(0));
			assertEquals(3, (int)argYList.get(1));
			assertEquals(5, (int)argYList.get(2));

		}};
	}

	//Example of confirming instance creation with withCapture
	class Person {
		public Person(String name , int age) {
		}
	}
	@Test
	public void test_v5(@Mocked Person mockPerson) {
		new Person("Joe", 10);
		new Person("Sara", 15);
		new Person("Jack", 99);

		//
		new Verifications() {{
			List<Person> created = withCapture(new Person(anyString, anyInt));
			assertEquals(3, created.size());

		}};
	}

Faking API The Faking API provides support for creating Fake implementations. Fake typically targets some methods and constructors in the class being Fake, and most other methods and constructors remain unchanged.

public / protected method Fake

In the following example, only Proc1 of the class in which Proc1 and Proc2 exist is Fake.

package jmockittest;

import static org.junit.Assert.*;

import org.junit.Test;

import mockit.Mock;
import mockit.MockUp;

public class FakeTest {
	class ClassA {
		protected String Proc1() {
			return "...Proc1";
		}
		public String Proc2() {
			return  "Proc2:" + this.Proc1();
		}

	}
	@Test
	public void test1() {
        new MockUp<ClassA>() {

        	@Mock
        	String Proc1() {
        		System.out.print("Proc1");
        		return "xxx";
        	}
        };
        ClassA obj = new ClassA();
        assertEquals("Proc2:xxx", obj.Proc2());
	}
}

Private method Fake

Not possible with 1.48. I get the following error:

java.lang.IllegalArgumentException: Unsupported fake for private method ClassA#Proc1()Ljava/lang/String; found
	at jmockittest.FakeTest$1.<init>(FakeTest.java:22)
	at jmockittest.FakeTest.test1(FakeTest.java:22)

Probably, it seems that it was made before and can not be done. https://github.com/jmockit/jmockit1/issues/605

Static method example

Fake of Static method is possible. The example below is an example where java.lang.Math.random always returns a fixed value.

	@Test
	public void test() {
        new MockUp<java.lang.Math>() {
        	@Mock
        	public double random() {
        		//Always 2.Returns 5 random()Method
        		return 2.5;
        	}
        };
		assertEquals(2.5, Math.random(), 0.1);
		assertEquals(2.5, Math.random(), 0.1);
	}

Is it possible to create a Fake for a method that has finail specified?

It was possible to create.

	class ClassB {
		final protected String Proc1() {
			return "...Proc1";
		}
		public String Proc2() {
			return  "Proc2:" + this.Proc1();
		}

	}
	@Test
	public void test3() {
        new MockUp<ClassB>() {

        	@Mock
        	String Proc1() {
        		System.out.print("Proc1");
        		return "xxx";
        	}
        };
        ClassB obj = new ClassB();
        assertEquals("Proc2:xxx", obj.Proc2());
	}

Special methods in the Fake class

There are \ $ init, \ $ clinic, \ $ advice as special methods in the Fake class. \ $ init targets the constructor. \ $ clinic is intended for static initializers. \ $ Advice represents all the methods of the target class.

** Test target **

ClassC.java


package SampleProject;

public class ClassC {
	public static int sx;
	private int x;
	static {
		sx = 999;
	}
	public ClassC(int x) {
		this.x = x;
	}

	public String Proc1() {
		System.out.format("ClassC Proc1 %d %d\n", sx, this.x);
		return "...Proc1";
	}

}

** Test code **

	@Test
	public void test4() {
        new MockUp<ClassC>() {
        	@Mock
        	void $clinit() {
        		//Confirm that static initialization of ClassiC is not working
        		assertEquals(0, ClassC.sx);
        	}

        	@Mock
        	void $init(int x) {
        		assertEquals(100, x);
        	}

        	@Mock
        	Object $advice(Invocation inv) {
				return "test";
        	}
        };
        ClassC obj = new ClassC(100);
        assertEquals("test", obj.Proc1());

	}

Special parameters of the Fake method

It is possible to use [Invocation](using #invocation) as the first parameter of the Fake method. Below is a sample that uses this to return a fixed value for the current time.

	@Test
	public void testTime() {
		Calendar nowCalendar = Calendar.getInstance();
		System.out.println("Current date and time: " + nowCalendar.getTime());
        new MockUp<Calendar>() {
        	@Mock
        	Calendar getInstance(Invocation inv) {
        		Calendar cal = inv.proceed();
        		cal.set(Calendar.YEAR, 2018);
        		cal.set(Calendar.MONTH, 0);
        		cal.set(Calendar.DAY_OF_MONTH, 1);
        		cal.set(Calendar.HOUR, 22);
        		cal.set(Calendar.MINUTE, 32);
        		cal.set(Calendar.SECOND, 12);
        		cal.set(Calendar.MILLISECOND, 512);
        		return cal;
        	}
        	@Mock
        	Calendar getInstance(Invocation inv, TimeZone zone, Locale aLocale) {
        		Calendar cal = inv.proceed();
        		cal.set(Calendar.YEAR, 2018);
        		cal.set(Calendar.MONTH, 0);
        		cal.set(Calendar.DAY_OF_MONTH, 1);
        		cal.set(Calendar.HOUR, 22);
        		cal.set(Calendar.MINUTE, 32);
        		cal.set(Calendar.SECOND, 12);
        		cal.set(Calendar.MILLISECOND, 512);
        		return cal;
        	}
        };
        final Calendar c = Calendar.getInstance();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        assertEquals("20180102103212512", sdf.format(c.getTime()));

	}

Coverage measurement

Coverage measurement results can be output by giving VM arguments in the execution configuration.

-Dcoverage-output=html -Dcoverage-srcDirs=..\SampleProject\src

image.png

image.png

image.png

See below for other arguments. http://jmockit.github.io/tutorial/CodeCoverage.html

As an undocumented behavior, "-Dcoverage-output = xml" seems to output XML.

Summary

I've researched so far, but looking at the discussion around the private method on GitHub and the abolition history of the update history, I feel that it is a little risky to use if you are not in a complete and perfect ideal test environment. ..

In addition, I also checked powermock + mockito below.

** Try using powermock-mockito2-2.0.2 ** https://qiita.com/mima_ita/items/3574a03b3379fb5f3c3c

Recommended Posts

Try using jmockit 1.48
Try using libGDX
Try using Maven
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
[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
Beginners try using android studio Part 2 (event processing)
Try local file search using Fess on CentOS7
Try local file search using Fess on CentOS8