如何运行并发单元测试?

jan*_*ith 44 concurrency junit unit-testing

如何使用junit运行并发测试?

假设我有一堂课

public class MessageBoard
{
    public synchronized void postMessage(String message)
    {
        ....
    }

    public void updateMessage(Long id, String message)
    {
        ....
    }
}
Run Code Online (Sandbox Code Playgroud)

我想同时测试对这个postMessage的多次访问.有什么建议吗?我希望针对我的所有setter函数(或任何涉及create/update/delete操作的方法)运行这种并发性测试.

Bri*_*new 21

不幸的是,我不相信你可以通过使用运行时测试来明确证明你的代码是线程安全的.你可以根据需要抛出任意数量的线程,它可能会/可能不会通过,具体取决于调度.

也许您应该查看一些静态分析工具,例如PMD,它们可以确定您如何使用同步并识别使用问题.

  • +1用于建议使用静态代码分析器.谷歌搜索揭示了许多可能性[java并发静态分析]. (5认同)

Pet*_*ter 16

我建议使用MultithreadedTC - 由并发大师自己Bill Pugh(和Nat Ayewah)撰写.从他们的概述报价:

MultithreadedTC是一个用于测试并发应用程序的框架.它具有一个节拍器,用于精确控制多线程中的活动顺序.

此框架允许您在单独的测试中确定性地测试每个线程交错


dfa*_*dfa 9

您只能证明并发错误的存在,而不是缺席.

但是,您可以编写一个专门的测试运行器来生成多个并发线程,然后调用@Test带注释的方法.

  • >(通过测试)"你只能证明并发错误的存在,而不是缺席."从一般意义上讲,即使对于非并发错误也是如此. (18认同)
  • 这有点真实,但通过传统的单元测试,您可以证明(当前)缺少*特定*错误.在测试并发问题时,你甚至没有那么多. (5认同)

Jud*_*ngo 7

在.NET中,有一些工具,如TypeMock RacerMicrosoft CHESS,专门用于单元测试并发.这些工具不仅可以找到死锁等多线程错误,还可以为您提供重现错误的线程交错集.

我想像Java世界有类似的东西.


sti*_*vlo 5

并发运行可能会导致意外结果.例如,我刚刚发现,虽然我的测试套件有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 {
...
Run Code Online (Sandbox Code Playgroud)

您还可以同时运行所有测试类:

import com.mycila.junit.concurrent.ConcurrentSuiteRunner;

@RunWith(ConcurrentSuiteRunner.class)
@Suite.SuiteClasses({ATest.class, ATest2.class, ATest3.class})
public class MySuite {
}
Run Code Online (Sandbox Code Playgroud)

Maven的依赖关系是:

<dependency>
    <groupId>com.mycila</groupId>
    <artifactId>mycila-junit</artifactId>
    <version>1.4.ga</version>
</dependency> 
Run Code Online (Sandbox Code Playgroud)

我目前正在研究如何多次运行方法并同时使用此包.如果有人有一个例子让我知道,可能已经可以在我的自制解决方案之下了.

@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
}
Run Code Online (Sandbox Code Playgroud)

正如其他人所指出的那样,你永远无法确定是否会出现并发错误,但是数以万计,或者数十万次执行并发,例如16,统计数据就在你身边.