所以我有一个启动五个线程的方法。我想写一个单元测试来检查五个线程是否已经启动。我怎么做?示例代码非常感谢。
2 回答
Instead of writing your own method to start threads, why not use an Executor
, which can be injected into your class? Then you can easily test it by passing in a dummy Executor
.
Edit: Here's a simple example of how your code could be structured:
public class ResultCalculator {
private final ExecutorService pool;
private final List<Future<Integer>> pendingResults;
public ResultCalculator(ExecutorService pool) {
this.pool = pool;
this.pendingResults = new ArrayList<Future<Integer>>();
}
public void startComputation() {
for (int i = 0; i < 5; i++) {
Future<Integer> future = pool.submit(new Robot(i));
pendingResults.add(future);
}
}
public int getFinalResult() throws ExecutionException {
int total = 0;
for (Future<Integer> robotResult : pendingResults) {
total += robotResult.get();
}
return total;
}
}
public class Robot implements Callable<Integer> {
private final int input;
public Robot(int input) {
this.input = input;
}
@Override
public Integer call() {
// Some very long calculation
Thread.sleep(10000);
return input * input;
}
}
And here's how you'd call it from your main()
:
public static void main(String args) throws Exception {
// Note that the number of threads is now specified here
ExecutorService pool = Executors.newFixedThreadPool(5);
ResultCalculator calc = new ResultCalculator(pool);
try {
calc.startComputation();
// Maybe do something while we're waiting
System.out.printf("Result is: %d\n", calc.getFinalResult());
} finally {
pool.shutdownNow();
}
}
And here's how you'd test it (assuming JUnit 4 and Mockito):
@Test
@SuppressWarnings("unchecked")
public void testStartComputationAddsRobotsToQueue() {
ExecutorService pool = mock(ExecutorService.class);
Future<Integer> future = mock(Future.class);
when(pool.submit(any(Callable.class)).thenReturn(future);
ResultCalculator calc = new ResultCalculator(pool);
calc.startComputation();
verify(pool, times(5)).submit(any(Callable.class));
}
Note that all this code is just a sketch which I have not tested or even tried to compile yet. But it should give you an idea of how the code can be structured.
与其说要“测试五个线程是否已经启动”,倒不如退一步想想这五个线程到底应该做什么。然后进行测试以确保“某事”实际上正在完成。
如果您真的只是想测试线程是否已启动,您可以做一些事情。您是否在某处保留对线程的引用?如果是这样,您可以检索引用,计算它们,然后调用isAlive()
每个引用(检查它是否返回true
)。
我相信在某个 Java 平台类上有一些方法,您可以调用它来查找正在运行的线程数,或者查找在 a 中运行的所有线程ThreadGroup
,但是您必须搜索以找出它是什么。
回应您的评论的更多想法
如果您的代码很简单new Thread(runnable).start()
,我就不会费心去测试线程是否真正开始了。如果您这样做,您基本上只是在测试 Java 平台是否有效(它确实有效)。如果您用于初始化和启动线程的代码更复杂,我会删除该thread.start()
部分并确保使用正确的参数等调用存根所需的次数。
不管你怎么做,我肯定会测试在多线程模式下运行时任务是否正确完成。从个人经验来看,我可以告诉你,一旦你开始用线程做任何远程复杂的事情,就很容易得到一些微妙的错误,这些错误只在某些条件下才会出现,而且可能只是偶尔出现。处理多线程代码的复杂性是一个非常滑的斜坡。
因此,如果你能做到,我强烈建议你做的不仅仅是简单的单元测试。在多线程、多核机器、非常大的数据集上运行任务的压力测试,并确保所有答案都完全符合预期。
此外,尽管您期望使用线程会提高性能,但我强烈建议您使用不同数量的线程对程序进行基准测试,以确保实际实现所需的性能提升。根据您的系统的设计方式,可能会遇到并发瓶颈,这可能会使您的程序在使用线程时几乎不会比不使用线程快。在某些情况下,它甚至可能更慢!