您的位置:首页 > 其它

JMockit简单使用(二)

2015-12-20 19:43 966 查看
       JMockit使用时,建议使用Expectations{}块来录制行为,这样mock对象在运行时必须 依据Expectations块中定义的顺序依次调用方法,不能多调用也不能少调用,可以省略掉Verifications块。一旦多调用或则少调用,那么测试就不会通过,这样可以清晰的把握单元测试每一步、每一次执行的过程。

       但是,测试的方法中如果存在线程的调用来执行录制行为,很多情况用Expectations{}块测试都不会通过,而使用NonStrictExpectations{}块来录制就不会有问题。

示例:

测试目标:
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()结束前依然没被回放,因此就会报错。

怎么解决呢?

       在work.task()调用后sleep()一段时间,以等runnable中任务被执行了再结束testTask()不就可以了么。这也是一个办法。但最好的解决方式是mock executor的execute()方法,让它不执行runnable.start(),而是执行runnable.run(),如下:
@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();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: