Unit testing 如何运行并发单元测试?

Unit testing 如何运行并发单元测试?,unit-testing,concurrency,junit,Unit Testing,Concurrency,Junit,如何使用junit运行并发测试 假设我有一节课 public class MessageBoard { public synchronized void postMessage(String message) { .... } public void updateMessage(Long id, String message) { .... } } 我想同时测试对这个postMessage的多重访问。 有什么建议

如何使用junit运行并发测试

假设我有一节课

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

    public void updateMessage(Long id, String message)
    {
        ....
    }
}
我想同时测试对这个postMessage的多重访问。
有什么建议吗?我希望对我的所有setter函数(或涉及创建/更新/删除操作的任何方法)运行这种并发测试。

不幸的是,我不相信您可以通过运行时测试来最终证明您的代码是线程安全的。您可以对它抛出任意多个线程,根据调度情况,它可能会通过,也可能不会通过


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

您只能证明并发错误的存在,而不能证明它们的缺失

但是,您可以编写一个专门的测试运行程序,生成几个并发线程,然后调用@test注释的方法

支持Java中的并发测试。这描述了如何使用它,testng站点上有文档


不确定是否可以在同一时间运行相同的测试,不过这里最好的方法是使用系统测试在请求中对代码进行爆破,以查看代码是否失效。然后使用单元测试来检查逻辑正确性。我的方法是为异步调用创建一个代理,并让它在测试中同步进行


但是,如果您想在完全安装和完整环境之外的级别执行此操作,您可以在junit中通过在单独的线程中创建对象来执行此操作,然后创建大量线程,向对象发出请求并阻止主线程,直到它完成。这种方法可能会使测试间歇性地失败,如果你做得不对。

在.NET中,有一些工具,如或,专门为单元测试并发性而设计。这些工具不仅可以找到死锁之类的多线程错误,还可以为您提供一组重现错误的线程交错


我想Java世界也有类似的情况。

在您的示例中,postMessage()方法是同步的,因此您实际上不会在单个VM中看到任何并发效果,但您可以评估同步版本的性能

您需要在不同的VM中同时运行测试程序的多个副本。你可以用

如果你不能让你的测试框架去做,你可以自己启动一些虚拟机。 process builder的东西对于路径之类的东西来说是一件痛苦的事情,但这里是一个大致的示意图:

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();
}
进程运行[]=新进程[5];
对于(int i=0;i<5;i++){
ProcessBuilder b=newProcessBuilder(“java-cp”+getCP()+“MyTestRunner”);
运行[i]=b.start();
}
对于(int i=0;i<5;i++){
正在运行[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**
public void testMesageBoard(){
最终留言板b=新留言板();
int n=5;
螺纹T[]=新螺纹[n];
对于(int i=0;i
测试并发错误是不可能的;您不仅需要验证输入/输出对,还必须在测试期间可能发生或不发生的情况下验证状态。不幸的是,JUnit无法做到这一点。

我建议使用并发主机自己编写的-和。引述他们的话:

多线程的EDTC是一个 测试并发应用程序。信息技术 功能节拍器,用于 提供对序列的精细控制 在多个线程中对活动进行排序


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

您可以使用tempus fugit库并行运行测试方法,并多次运行测试方法,以模拟负载测试类型的环境。尽管之前的评论指出,post方法是同步的,因此受到保护,但可能涉及到自身未受保护的相关构件或方法,因此负载/浸泡类型测试可能会捕捉到这些构件或方法。我建议您设置一个相当粗粒度的/端到端的测试,以使您获得捕获任何循环漏洞的最佳机会

请参阅的JUnit集成部分


顺便说一句,我是上述项目的开发人员:)

试着看看JUnit附带的ActiveTestSuite。它可以同时启动多个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次。如果您想要并行测试中的变化,只需创建一个不同的类。

并发运行可能会导致意外的结果。例如,我刚刚发现,虽然我的测试套件有200个测试,在逐个执行时通过,但并发执行失败,我发现这不是线程安全问题,而是依赖于另一个测试的测试,这是一件坏事,我可以解决问题

这很有趣。与最新的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 {
}
最重要的是:

<代码
<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
}