首页 > 解决方案 > 如何在 JMockit 中正确模拟私有 ExecutorService 的提交方法

问题描述

我有一个包含私有 ExecutorService 实例的类。在类中,我有一个运行提交方法并捕获 RejectedExecutionException 的方法。但是,我在模拟 ExecutorService 实例以引发异常以完成测试覆盖时遇到了麻烦。我正在使用 JMockit 1.45。

我已经浏览过 JMockit 教程和其他网站;无论我使用@Mocked、@Capturing,还是创建一个新的假类,它似乎都不起作用。

// Implemented Class:
public class TaskRegister {

    private ExecutorService executor;

    public TaskRegister() {
        this.executor = Executors.newFixedThreadPool(5);
    }

    public void executeTask(Runnable task) {
        try {
            this.executor.submit(task);
        } catch (RejectedExecutionException e) {
            System.out.println(e.getMessage);
        }
    }
}


// Unit Test Class:
public class TestTaskRegister {
    @Tested
    private TaskRegister tested;

    private static int counter;

    @Test // this works
    public void runNormalTask() throws InterruptedException {
        counter = 0;
        Runnable mockTask = new Runnable() {
            counter++;
        }

        tested.executeTask(mockTask);
        Thread.sleep(100); // Allow executor to finish other thread.
        assertEquals(1, counter);
    }

    @Test // this doesn't work, will have missing invocation error.
    public void throwsError (@Capturing ExecutorService executor) throws InterruptedException {
        counter = 0;

        // somehow the tested class still runs the actual executor 
        // and not the mocked one.
        new Expectations() {{
             executor.submit((Runnable) any);
             result = new RejectedExecutionException();
        }};

        Runnable mockTask = new Runnable() {
            // some task
        }

        tested.executeTask(mockTask);
        Thread.sleep(100);
        assertEquals(0, counter);
    }
}

我希望@Capturing 拦截真正的执行器实现并在调用 executor.submit 时抛出异常,但它没有这样做。

标签: javajava.util.concurrentjmockit

解决方案


Mocking by@Capturing可能很昂贵,并且在某些情况下可能会导致意外结果,因此(当前)所有java.*类都被排除在外。所以,java.util.concurrent.ThreadPoolExecutor在这个测试中不会被嘲笑(可以用@Mocked)。

在实践中,RejectedExecutionException异常永远不会发生(ThreadPoolExecutor至少不会发生 - 可能只有 a ForkJoinPool)。所以,这个测试是不值得的。实际上,由于该异常是 aRuntimeException您可以简单地catch完全删除该块。

这是(滥用)使用模拟库时发生的坏事之一:人们有时使用它们来测试不可能的情况,因此编写无用的测试。


推荐阅读