如何使用JUnit进行并发性测试?
比方说,我有一个类
public class MessageBoard
{
public synchronized void postMessage(String message)
{
....
}
public void updateMessage(Long id, String message)
{
....
}
}
我婉同时测试此postMessage的多址接入。 在这个有什么建议? 我想运行这种对我所有的setter函数的并发测试(或涉及创建/更新/删除操作的任何方法)的。
不幸的是,我不相信你可以明确地证明你的代码是线程安全的使用运行时测试。 只要你喜欢反对它甚至可以把尽可能多的线程,它可以/取决于调度可能无法通过。
也许你应该看一些静态分析工具,如PMD ,可以确定您如何使用同步和标识的使用问题。
我会建议使用MultithreadedTC -写的并发大师亲自比尔·皮尤 (和纳特艾瓦赫 )。 从他们的报价概述 :
MultithreadedTC是测试并发应用程序的框架。 其特点是,用于提供对在多个线程活动的顺序的精细控制的节拍器。
该框架允许你确定性测试每个线程在单独的测试交织
你只能证明并发错误的存在,而不是他们的缺席 。
但是你可以写一个专门的测试运行器会派生多个并发线程,然后调用你的@Test注解的方法。
在.NET中,有喜欢的工具TypeMock赛车或微软CHESS了为单元测试并发性而设计的。 这些工具不仅可以找到多线程蝽象死锁,但也给你一套重现错误线交错的。
我想像有一个为Java世界中类似的东西。
同时运行可能会导致意想不到的结果。 举例来说,我才发现,虽然我的测试套件200个测试通过时,由一个执行的一个,它不能并发执行,我挖的是,它不是一个线程安全的问题,而是一个测试依赖于另外一个,这是一件坏事,我可以解决这个问题。
JUnit的ConcurrentJunitRunner和ConcurrentSuite Mycila工作是很有趣的。 相比最新的GA版的文章似乎有点过时了,在我的例子,我将显示更新的用法。
注释测试类像下面将导致并行执行的测试方法,用6并发级别:
import com.mycila.junit.concurrent.ConcurrentJunitRunner;
import com.mycila.junit.concurrent.Concurrency;
@RunWith(ConcurrentJunitRunner.class)
@Concurrency(6)
public final class ATest {
...
您也可以同时运行所有测试类:
import com.mycila.junit.concurrent.ConcurrentSuiteRunner;
@RunWith(ConcurrentSuiteRunner.class)
@Suite.SuiteClasses({ATest.class, ATest2.class, ATest3.class})
public class MySuite {
}
该Maven的依赖关系是:
<dependency>
<groupId>com.mycila</groupId>
<artifactId>mycila-junit</artifactId>
<version>1.4.ga</version>
</dependency>
我目前正在研究如何与这个包运行方法多次,兼任。 它可能已经,如果任何人有一个例子让我知道,我下面的解决方案家酿。
@Test
public final void runConcurrentMethod() throws InterruptedException {
ExecutorService exec = Executors.newFixedThreadPool(16);
for (int i = 0; i < 10000; i++) {
exec.execute(new Runnable() {
@Override
public void run() {
concurrentMethod();
}
});
}
exec.shutdown();
exec.awaitTermination(50, TimeUnit.SECONDS);
}
private void concurrentMethod() {
//do and assert something
}
正如其他人所指出的,是事实,你永远无法确定并发错误是否会出现与否,而是与成千上万十个,或者,一并发执行数千百说16,统计就在你身边。
试着看一下ActiveTestSuite其中附带的JUnit。 它可以同时启动多个JUnit测试:
public static Test suite()
{
TestSuite suite = new ActiveTestSuite();
suite.addTestSuite(PostMessageTest.class);
suite.addTestSuite(PostMessageTest.class);
suite.addTestSuite(PostMessageTest.class);
suite.addTestSuite(PostMessageTest.class);
suite.addTestSuite(PostMessageTest.class);
return suite;
}
上述将使用并联运行相同的JUnit测试类的5倍。 如果您在paralell测试要变化,只需要创建一个不同的类。
在您的示例的postMessage()方法是同步的 ,所以你不会真正看到从一个单一的虚拟机中的任何并发影响,但你也许可以评估的同步版本的性能。
您将需要在不同虚拟机同时运行测试程序的多个副本。 您可以使用
如果你不能让你的测试框架,这样做就可以发动一些虚拟机你的自我。 该进程生成的东西是路径和诸如此类的东西很烦,但这里是一般的草图:
Process running[] = new Process[5];
for (int i = 0; i < 5; i++) {
ProcessBuilder b = new ProcessBuilder("java -cp " + getCP() + " MyTestRunner");
running[i] = b.start();
}
for(int i = 0; i < 5; i++) {
running[i].waitFor();
}
我通常不喜欢这样简单的线程测试,像其他人发布,测试是不是正确的证明,但它通常动摇了愚蠢的错误实践中。 它有助于在各种不同条件下测试了很长时间 - 有时并发错误需要一段时间才能体现在测试。
public void testMesageBoard() {
final MessageBoard b = new MessageBoard();
int n = 5;
Thread T[] = new Thread[n];
for (int i = 0; i < n; i++) {
T[i] = new Thread(new Runnable() {
public void run() {
for (int j = 0; j < maxIterations; j++) {
Thread.sleep( random.nextInt(50) );
b.postMessage(generateMessage(j));
verifyContent(j); // put some assertions here
}
}
});
PerfTimer.start();
for (Thread t : T) {
t.start();
}
for (Thread t : T) {
t.join();
}
PerfTimer.stop();
log("took: " + PerfTimer.elapsed());
}
}**strong text**
测试并发错误是不可能的; 你不只是要验证输入/输出对的,但你有可能会或可能不会在您的测试中发生的情况,以验证状态。 不幸的是JUnit是不具备这样做。
您可以使用的Tempus-福吉特库并行运行,并多次测试方法来模拟负载测试类型的环境。 虽然之前的评论指出,POST方法syncrhonised等保护,相关成员或方法可以涉及其自身得不到保护,所以它可能是一个负载/浸泡型式试验能抓住这些。 我建议你设置一个相当粗粒/终端到终端的测试一样给你捕捉任何环洞的最好机会。
请参阅的JUnit的集成部分文档 。
顺便说一句,我一开发商在上述项目:)
TestNG的具有对Java并发测试支持。 该文章中描述了如何可以使用,并且没有对TestNG的网站文档。
不知道你是否可以在同一时间做同样的测试运行,虽然
这里最好的方法是使用系统测试与请求爆你的代码,看看它是否倒下。 然后使用单元测试,以检查逻辑正确性。 我将接近这一问题的方法是创建用于异步调用的代理,并把它在测试同步建立。
但是,如果你想在其他的水平,而不是做一个完整的安装和完整的环境,你可以通过一个单独的线程创建的对象做到这一点在JUnit中,然后创建大量线程的那团火请求你的对象,并阻塞主线程直到它完成。 这种方法可以使测试失败间歇如果你没有得到它的权利。
您也可以尝试HavaRunner 。 它默认运行在平行试验。