JMockit简单使用(二)

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



  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JMockit is a Java library that provides support for mocking and testing. The @Qualifier annotation is used in JMockit to identify a specific instance of a bean to be used in a test. In Spring, the @Qualifier annotation is used in a similar way to identify a specific bean to be injected into a component. However, in JMockit, the @Qualifier annotation is used in conjunction with other annotations to specify which instance of a mock or spy object to use in a test. For example, consider a scenario where we have two implementations of a service interface and we want to mock one of them for testing. We can use the @Qualifier annotation to identify the bean to be mocked and the @Mocked annotation to create a mock object of that bean. ``` public interface MyService { String getName(); } @Service("fooService") public class FooService implements MyService { @Override public String getName() { return "Foo"; } } @Service("barService") public class BarService implements MyService { @Override public String getName() { return "Bar"; } } public class MyServiceTest { @Test public void testGetName(@Mocked @Qualifier("fooService") MyService fooService, @Mocked @Qualifier("barService") MyService barService) { new Expectations() {{ fooService.getName(); result = "Mocked Foo"; barService.getName(); result = "Mocked Bar"; }}; // Use the mocked instances of fooService and barService in the test // ... } } ``` In the above example, we have two implementations of the MyService interface, FooService and BarService, and we want to mock FooService for testing. We use the @Qualifier("fooService") annotation to identify the bean to be mocked and the @Mocked annotation to create a mock object of that bean. We also create a mock object of the BarService bean using the @Mocked and @Qualifier("barService") annotations. We can then use these mocked instances of the beans in our test.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值