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

2022-09-01 20:20:49

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

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

    ExecutorService executorService = Executors.newSingleThreadExecutor();
    executorService.submit(R);
}

当我进行单元测试时,我想对该方法执行的一些验证进行验证。

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

在我的单元测试中,我必须等到此方法完成执行。有没有更好的方法可以做到这一点,而不是等待.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);
}

注意:我正在将执行器服务对象传递到此构造函数类中。我想知道是否有一种很好的测试方法,而不是等待睡眠时间。


答案 1

您也可以自己实现一个执行器服务,它将在同一线程中运行任务。例如:

public class CurrentThreadExecutor implements Executor {
    public void execute(Runnable r) {
        r.run();
    }
}

然后,您可以从中继承并使用此实现。AbstractExecutorService

如果您使用的是番石榴,另一个简单的方法是使用MoreExecutors.newDirectExecutorService(),因为它可以做同样的事情,而无需您自己创建一个。


答案 2

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

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

您可以修改类构造函数,或添加仅在测试中使用的新构造函数,这样您就可以提供自己的或 .然后从 中传入一个。ExecutorExecutorServiceMoreExecutors

因此,在测试文件中,您将使用MoreExecutors

ExecutorService mockExecutor = MoreExecutors.newDirectExecutorService();

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

MyService myService = new MyService(mockExecutor);

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

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

推荐