Java 如何使用线程执行单元测试?
执行摘要:当在线程中抛出断言错误时,单元测试不会消亡。这是有道理的,因为不应该允许一个线程使另一个线程崩溃。问题是:1)当第一个帮助线程崩溃时,如何使整个测试失败;2)在所有线程都完成后循环并确定每个线程的状态(参见下面的代码)。后一种方法是使用每个线程的状态变量,例如“boolean[]status”和“status[i]==false”,这意味着线程失败(这可以扩展以捕获更多信息)。然而,这并不是我想要的:我希望它在抛出断言错误时像任何其他单元测试一样失败。这可能吗?这是可取的吗 我觉得很无聊,我决定在我的单元测试中产生一堆线程,然后让它们调用一个服务方法,只是为了好玩。代码大致如下所示:Java 如何使用线程执行单元测试?,java,multithreading,unit-testing,junit,Java,Multithreading,Unit Testing,Junit,执行摘要:当在线程中抛出断言错误时,单元测试不会消亡。这是有道理的,因为不应该允许一个线程使另一个线程崩溃。问题是:1)当第一个帮助线程崩溃时,如何使整个测试失败;2)在所有线程都完成后循环并确定每个线程的状态(参见下面的代码)。后一种方法是使用每个线程的状态变量,例如“boolean[]status”和“status[i]==false”,这意味着线程失败(这可以扩展以捕获更多信息)。然而,这并不是我想要的:我希望它在抛出断言错误时像任何其他单元测试一样失败。这可能吗?这是可取的吗 我觉得很无
Thread[] threads = new Thread[MAX_THREADS];
for( int i = 0; i < threads.length; i++ ) {
threads[i] = new Thread( new Runnable() {
private final int ID = threadIdSequenceNumber++;
public void run() {
try {
resultRefs[ID] = runTest( Integer.toString( ID ) ); // returns an object
}
catch( Throwable t ) {
// this code is EVIL - it catches even
// Errors - don't copy it - more on this below
final String message = "error testing thread with id => "
+ ID;
logger.debug( message, t );
throw new IllegalStateException( message, t );
// need to wrap throwable in a
// run time exception so it will compile
}
}
} );
}
Thread[]threads=新线程[MAX_threads];
对于(int i=0;i的线程时出错”
+身份证;
调试(消息,t);
抛出新的IllegalStateException(消息,t);
//需要将一次性物品包装在
//运行时异常,因此它将编译
}
}
} );
}
在此之后,我们将循环遍历线程数组并启动每个线程。之后,我们将等待他们全部完成。最后,我们将对结果引用执行一些检查
for( Thread thread : threads )
thread.start();
logger.debug( "waiting for threads to finish ..." );
boolean done = false;
while( !done ) {
done = true;
for( Thread thread : threads )
if( thread.isAlive() )
done = false;
}
for( int i = 0; i < resultRefs.length; i++ ) {
assertTrue( "you've got the world messed, dawg!",
myCondition(resultRefs[i]) );
for(线程:线程)
thread.start();
debug(“等待线程完成…”);
布尔完成=假;
而(!完成){
完成=正确;
用于(线程:线程)
if(thread.isAlive())
完成=错误;
}
for(int i=0;i
问题就在这里。你注意到那个讨厌的try-catch-throwable块了吗?我只是添加了它作为一个临时黑客,这样我就可以看到发生了什么。在runTest(String)中,有一些断言,例如assertNotNull(null),但由于它位于不同的线程中,所以不会导致单元测试失败
我的猜测是,我们将需要以某种方式迭代线程数组,检查每个线程的状态,如果线程以令人讨厌的方式终止,则手动导致断言错误。提供此信息的方法(死线程的堆栈跟踪)的名称是什么.并发性是很难进行单元测试的事情之一。如果您只是尝试测试每个线程中的代码是否正在执行它应该测试的操作,那么您可能应该只测试与上下文无关的代码。 如果在本例中,线程协作以达到某个结果,那么您可以在不使用线程的情况下测试该协作。这可以通过顺序执行所有协作部分来完成。 如果你想测试比赛条件和诸如此类的东西,单元测试不是最好的方法。你会得到有时失败,有时不失败的测试。 总而言之,我认为您的问题可能是单元测试的级别太高。
希望这有帮助谷歌测试博客有一篇关于这个主题的优秀文章,非常值得一读:
它是用Python编写的,但我认为这些原则可以直接移植到Java。在多线程环境中进行单元测试是困难的…因此需要进行一些调整。单元测试必须是可重复的..确定性的。因此,任何具有多线程的测试都不符合这一标准。具有多线程的测试也往往很慢
- 我要么试着看看我是否能在一个线程上进行测试..被测试的逻辑真的需要多个线程吗
- 如果这不起作用,那么使用成员变量方法,在测试结束时,当所有线程都完成运行时,可以根据预期值进行检查
您的可运行包装器应该将异常对象传递回您的测试类,然后您可以将它们存储在集合中。当所有测试完成后,您可以测试集合。如果集合不是空的,则迭代每个异常,.printStackTrace()然后失败。实现一个设置一些标志的(线程定期检查)和。Junit并发线程测试的另一个流行选项是Matthieu Carbou使用自定义JunitRunner和简单注释的方法
通过使用特殊的同步对象,可以使单元测试失败。请看以下文章: 我将在这里解释要点。 您希望能够将内部线程故障外部化到主线程,在您的情况下,主线程就是测试。因此,您必须使用内部线程和测试都将用于彼此同步的共享对象/锁。 请参阅以下测试-它创建一个线程,通过调用名为Spreader的共享对象来模拟抛出的异常。 Spreader.getInstance()上的主线程(测试)被阻塞。等待(上下文,10000) 在调用release时,它将被释放并捕获抛出的异常。 在catch块中,您可以编写测试失败的断言
@Test
public void testAwait_InnerThreadExternalizeException() {
final int CONTEXT = 1;
final String EXCEPTION_MESSAGE = "test inner thread exception message";
// release will occur sometime in the future - simulate exception in the releaser thread
ExecutorServiceFactory.getCachedThreadPoolExecutor().submit(new Callable<void>() {
@Override
public Void call() throws Exception {
Sprinkler.getInstance().release(CONTEXT, new RuntimeException(EXCEPTION_MESSAGE));
return null;
}
});
Throwable thrown = null;
try {
Sprinkler.getInstance().await(CONTEXT, 10000);
} catch (Throwable t) {
// if the releaser thread delivers exception it will be externelized to this thread
thrown = t;
}
Assert.assertTrue(thrown instanceof SprinklerException);
Assert.assertEquals(EXCEPTION_MESSAGE, thrown.getCause().getMessage());
}
@测试
P