JMockit簡単使用(二)

5339 ワード

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
ワークが呼び出されたのに.task()は、task()でgetValue()メソッドが呼び出されますが、Missing invocationsエラー(OtherVO#getValue()メソッドを少なくした)を報告するわけにはいきません.どうしてですか.なぜならスレッドの呼び出し、executor.execute()はスレッドにrunnableを実行するように通知するだけで、runnableでのタスクの実行にはJVMがスケジューリングする必要があり、いつ実行するかはtestTask()全体が終了した後に発生する可能性がありますが、Expectations{}は、testTask()が終了するまで録画動作が戻されていないことを検出し、エラーを報告します.
どうやって解決しますか?
仕事中です.task()呼び出し後sleep()しばらく、runnableでタスクが実行されてからtestTask()を終了すればよいのではないでしょうか.これも一つの方法だ.しかし、最良の解決策はmock executerの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();
    }
}
もう1つの簡単な方法:
@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();
    }
}