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
When unit testing with xUnit, the dependent parts can be a problem and difficult to test.
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.
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.
(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.
See below for details on how to do this: http://jmockit.github.io/tutorial/Introduction.html#runningTests
** Event **
** 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.
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
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.
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.
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.
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));
}
}
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.
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.
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.
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());
}
}
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.
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);
}
}
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.
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));
}
}
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);
}};
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.
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));
}
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.
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 |
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 |
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));
}
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));
}
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));
}
}
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"));
}
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());
}
}
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());
}
}
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));
}
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));
}
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
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);
}};
}
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.
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());
}
}
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
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);
}
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());
}
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());
}
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 results can be output by giving VM arguments in the execution configuration.
-Dcoverage-output=html -Dcoverage-srcDirs=..\SampleProject\src
See below for other arguments. http://jmockit.github.io/tutorial/CodeCoverage.html
As an undocumented behavior, "-Dcoverage-output = xml" seems to output XML.
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