喵♂呜 的博客

一个刚毕业就当爹的程序猿 正在迷雾中寻找道路...

如何在JUnit中运行多线程的单元测试

写过单元测试的同学应该遇到过这个问题 单元测试中新建的线程 一般都不会被运行 这是因为JUnit在底层实现上是用 System.exit() 退出的

问题原因

查看JUnit4的TestRunner的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static final int SUCCESS_EXIT = 0;
public static final int FAILURE_EXIT = 1;
public static final int EXCEPTION_EXIT = 2;

public static void main(String args[]) {
TestRunner aTestRunner = new TestRunner();
try {
TestResult r = aTestRunner.start(args);
if (!r.wasSuccessful())
System.exit(FAILURE_EXIT);
System.exit(SUCCESS_EXIT);
} catch (Exception e) {
System.err.println(e.getMessage());
System.exit(EXCEPTION_EXIT);
}
}

可以看到 单元测试执行完毕之后 是用 System.exit() 退出的主线程 主线程结束了 其他线程自然被终止了

解决方案

在Java的线程池中 有一个方法 awaitTermination() 可以检测所有线程是否执行完毕 可以通过循环判断来达到防止主线程退出的目的

下面提供一个工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package pw.yumc.MiaoServer.kits;

import java.io.File;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
* 线程池
* Created by 蒋天蓓 on 2017/4/8.
*/
public class ExecutorKit {
static ExecutorService executor = Executors.newCachedThreadPool();

public static void execute(Runnable run) {
executor.execute(run);
}

public static ThreadPoolExecutor getExecutor() {
return executor;
}

public static void shutdownAndWait() {
executor.shutdown();
boolean loop = false;
do { //等待所有任务完成
try {
// 阻塞,直到线程池里所有任务结束
loop = !executor.awaitTermination(2, TimeUnit.SECONDS);
} catch (InterruptedException ignored) {
}
} while (loop);
}
}

使用方法

新建一个单元测试类 贴上下列代码 用于打印 2016 年至今的所有日期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void threadTest(){
Calendar start = Calendar.getInstance();
start.set(2016, Calendar.JANUARY, 1, 0, 0, 0);
Calendar end = Calendar.getInstance();
// 用线程池执行线程
ExecutorKit.execute(() -> {
while (start.getTime().getTime() <= end.getTime().getTime()) {
System.out.println(start.getTime());
start.add(Calendar.DAY_OF_YEAR, 1);
}
});
// 阻塞检查线程 防止退出
ExecutorKit.shutdownAndWait();
}

经过测试 可以完整的打印所有的内容

欢迎关注我的其它发布渠道