package org.zero.jmockit; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; class OtherVO { private String value; public String getValue() { return value; } public void setValue(String value) { this.value = value; } } public class Work { private OtherVO otherVO; private ExecutorService executor = Executors.newFixedThreadPool(4); public void task() { executor.execute(new Runnable() { public void run() { otherVO.getValue(); } }); } }测试文件:
package org.zero.jmockit; import mockit.Deencapsulation; import mockit.Expectations; import mockit.Mocked; import mockit.Tested; import mockit.integration.junit4.JMockit; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(JMockit.class) public class WorkTest { @Tested @Mocked Work work; @Test public void testTask() { final OtherVO otherVO = new OtherVO(); new Expectations(OtherVO.class) { // mock OtherVO的行为 { otherVO.getValue(); result = "zero"; } }; new Expectations() { { Deencapsulation.setField(work, "otherVO", otherVO); } }; // --- 通过以上方式可以将mock出来的otherVO设置到work中,当然可以采取其它简便方式 work.task(); } }运行测试,会发现测试不通过,会抛出一下异常:
mockit.internal.MissingInvocation: Missing 1 invocation to: org.zero.jmockit.OtherVO#getValue() on mock instance: org.zero.jmockit.OtherVO@3419866c at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) Caused by: Missing invocations at org.zero.jmockit.OtherVO.getValue(Work.java) at org.zero.jmockit.WorkTest$1.<init>(WorkTest.java:23) at org.zero.jmockit.WorkTest.testTask(WorkTest.java:21) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.lang.reflect.Method.invoke(Method.java:497) ... 6 more明明调用了work.task(),task()里会调用getValue()方法,但是怎么会报Missing invocations错误(少调了OtherVO#getValue()方法)。为什么呢?原因在于线程的调用,executor.execute()只是通知线程去执行runnable,runnable中任务的执行需要JVM去调度,至于什么时候去执行,可能发生在整个testTask()结束后,而Expectations{}检测到录制行为在testTask()结束前依然没被回放,因此就会报错。
@RunWith(JMockit.class) public class WorkTest { @Tested @Mocked Work work; private ExecutorService executor; @Before public void initThread() { executor = new MockUp<ThreadPoolExecutor>() { @Mock public void execute(Runnable command) { command.run(); } }.getMockInstance(); } @Test public void testTask() { final OtherVO otherVO = new OtherVO(); new Expectations(OtherVO.class) { // mock OtherVO的行为 { otherVO.getValue(); result = "zero"; } }; new Expectations() { { Deencapsulation.setField(work, "otherVO", otherVO); } }; // --- 通过以上方式可以将mock出来的otherVO设置到work中,当然可以采取其它简便方式 new Expectations() { { Deencapsulation.setField(work, "executor", executor); // --- 将mock出来executor设置到work中 } }; work.task(); } }另一种简单方式:
@RunWith(JMockit.class) public class WorkTest { @Tested @Mocked Work work; @Test public void testTask() { final OtherVO otherVO = new OtherVO(); new Expectations(OtherVO.class) { // mock OtherVO的行为 { otherVO.getValue(); result = "zero"; } }; new Expectations() { { Deencapsulation.setField(work, "otherVO", otherVO); } }; // --- 通过以上方式可以将mock出来的otherVO设置到work中,当然可以采取其它简便方式 new MockUp<ThreadPoolExecutor>() { @Mock public void execute(Runnable command) { command.run(); } }; work.task(); } }