如何对执行器服务中运行的代码片段进行单元测试,而不是等待Thread.sleep(时间)

sam*_*uri 8 java unit-testing executorservice

如何对执行程序服务中运行的代码进行单元测试?在我的情况下,

public void test() {
    Runnable R = new Runnable() {
        @Override
        public void run() {
            executeTask1();
            executeTask2();
        }
    };

    ExecutorService executorService = Executors.newSingleThreadExecutor();
    executorService.submit(R);
}
Run Code Online (Sandbox Code Playgroud)

当我进行单元测试时,我想做一些方法执行的验证.

我正在执行器服务中执行此操作,因为它进行了一些网络操作.

在我的单元测试中,我不得不等到这个方法完成执行.有没有更好的方法可以做到这一点,而不是等待Thread.sleep(500).

单元测试代码段:

@Test
public void testingTask() {
    mTestObject.test();
    final long threadSleepTime = 10000L;
    Thread.sleep(threadSleepTime);
    verify(abc, times(2))
            .acquireClient(a, b, c);
    verify(abd, times(1)).addCallback(callback);
}
Run Code Online (Sandbox Code Playgroud)

注意:我将一个执行程序服务对象传递给此构造函数类.我想知道是否有一种好的测试方法而不是等待睡眠时间.

cla*_*dio 12

您也可以自己实现一个ExecutorService,它将在同一个线程中运行该任务.例如:

public class CurrentThreadExecutor implements Executor {
    public void execute(Runnable r) {
        r.run();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以继承AbstractExecutorService并使用这个实现.

如果您正在使用Guava,另一个简单的方法是使用MoreExecutors.newDirectExecutorService(),因为它可以完成同样的事情而无需您自己创建一个.


ejf*_*cis 9

Google Guava 提供了一个很棒的类MoreExecutors,它在测试通过JUnitExecutorExecutorService在并行线程中运行的代码时帮助了我。它允许您创建Executor仅在同一线程中运行所有内容的实例,本质上是作为真实Executor. 问题是当事情在 JUnit 不知道的其他线程中运行时,这些ExecutorsfromMoreExecutors使一切更容易测试,因为它实际上不是在另一个线程中并行的。

请参阅MoreExecutors文档https://google.github.io/guava/releases/19.0/api/docs/com/google/common/util/concurrent/MoreExecutors.html

你可以修改你的类构造函数,或者添加一个你只在测试中使用的新构造函数,这样你就可以提供自己的ExecutorExecutorService. 然后传入MoreExecutors.

因此,在测试文件中,您将使用以下命令创建模拟执行程序 MoreExecutors

ExecutorService mockExecutor = MoreExecutors.newDirectExecutorService();

// or if you're using Executor instead of ExecutorService you can do MoreExecutors.newDirectExecutor()

MyService myService = new MyService(mockExecutor);
Run Code Online (Sandbox Code Playgroud)

然后在你的类中,如果构造函数中没有提供它,你只创建一个真正的 Executor

public MyService() {}
    ExecutorService threadPool;

    public MyService(ExecutorService threadPool) {
        this.threadPool = threadPool;
    }

    public void someMethodToTest() {
        if (this.threadPool == null) {
            // if you didn't provide the executor via constructor in the unit test, 
            // it will create a real one
            threadPool = Executors.newFixedThreadPool(3);
        }
        threadPool.execute(...etc etc)
        threadPool.shutdown()
    }
}
Run Code Online (Sandbox Code Playgroud)


mrt*_*xaz 4

您可以使用 executorService.submit(R) 返回的 Future 实例。

来自文档:

https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html#submit(java.lang.Runnable)

提交一个 Runnable 任务来执行并返回一个表示该任务的 Future。Future 的 get 方法在成功完成后将返回 null。

例子:

@Test
void test() throws ExecutionException {
    Future<Boolean> result = Executors.newSingleThreadExecutor().submit(() -> {
        int answer = 43;
        assertEquals(42, answer);
        return true;
    }
    assertTrue(result.get());
}
Run Code Online (Sandbox Code Playgroud)

内部断言将抛出一个异常,这会导致result.get()抛出它自己的异常。因此测试将失败,并且异常的原因会告诉您原因(“预期为 42,但实际为 43”)。