如何运行并发unit testing?
如何使用junit来运行并发testing?
假设我有一堂课
public class MessageBoard { public synchronized void postMessage(String message) { .... } public void updateMessage(Long id, String message) { .... } }
我想同时testing多个访问这个postMessage。 对此有何build议? 我希望对我所有的setter函数(或涉及到创build/更新/删除操作的任何方法)运行这种并发性testing。
不幸的是,我不相信你可以通过使用运行时testing明确地certificate你的代码是线程安全的。 您可以根据需要抛出尽可能多的线程,并且根据调度可能会不会通过。
也许你应该看看一些静态分析工具,比如PMD ,它可以决定你如何使用同步和识别使用问题。
我build议使用MultithreadedTC – 由并发大师Bill Pugh (和Nat Ayewah ) 编写 。 从他们的概述引用:
MultithreadedTC是一个testing并发应用程序的框架。 它具有一个节拍器,用于对多个线程中的活动顺序进行精细控制。
这个框架允许你在单独的testing中确定性地testing每个线程交错
你只能certificate并发错误的存在,而不是他们的缺席 。
但是你可以写一个专门的testing运行器,产生几个并发的线程,然后调用你的@Test注释的方法。
在.NET中,像TypeMock Racer或Microsoft CHESS这样的工具专门为unit testing并发而devise。 这些工具不仅可以find像死锁这样的multithreading错误,还可以为您提供一组重现错误的线程交错。
我可以想象Java世界有类似的东西。
同时运行可能会导致意外的结果。 比如刚才我发现,虽然我的testing套件有200个testing通过的时候一个接一个执行,但是并发执行失败了,我挖了这个,不是线程安全问题,而是依赖于另外一个testing是一件坏事,我可以解决这个问题。
Mycila在JUnit ConcurrentJunitRunner和ConcurrentSuite上工作非常有趣。 与最新的GA版本相比,这篇文章看起来有些过时,在我的示例中,我将展示更新的使用情况。
注释如下的testing类将导致并发执行testing方法,并发级别为6:
import com.mycila.junit.concurrent.ConcurrentJunitRunner; import com.mycila.junit.concurrent.Concurrency; @RunWith(ConcurrentJunitRunner.class) @Concurrency(6) public final class ATest { ...
你也可以同时运行所有的testing类:
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>
我目前正在调查如何多次运行方法,并与这个包同时运行。 如果有人有一个例子让我知道,可能已经可能在我的自制解决scheme之下。
@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的统计信息就在您身边。
在你的例子中,postMessage()方法是同步的 ,所以你实际上不会看到单个虚拟机中的任何并发效应,但是你可能能够评估同步版本的性能。
您需要在不同的虚拟机上同时运行testing程序的多个副本。 您可以使用
如果你不能让你的testing框架做到这一点,你可以自己启动一些虚拟机。 过程build设者的东西是一个痛苦的path和whatnot,但这里是一般的草图:
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(); }
我通常为简单的线程化testing做这样的事情,就像其他人发布的一样,testing不是正确性的certificate,但是它通常会在实践中摆脱愚蠢的错误。 它有助于在各种不同的条件下进行很长时间的testing – 有时并发错误需要一段时间才能在testing中体现出来。
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**
testing并发错误是不可能的。 您不必validationinput/输出对,但必须在testing期间可能发生或可能不发生的情况下validation状态。 不幸的是JUnit没有装备这样做。
试着看看JUnit附带的ActiveTestSuite。 它可以同时启动多个JUnittesting:
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; }
以上将以并行方式运行相同的JUnittesting类5次。 如果你想在并行testing中做出变化,只需创build一个不同的类。
您可以使用tempus-fugit库并行运行testing方法并多次模拟负载testingtypes的环境。 尽pipe以前的评论指出post方法是同步的并且受到保护,但是可能涉及到相关的成员或方法本身不受保护,所以可能的是,load / soaktypes的testing可能会捕获这些方法。 我build议你设置一个相当粗糙的/端到端的类似testing,给你抓住任何循环漏洞的最佳机会。
请参阅文档的 JUnit集成部分。
顺便说一句,我是一个开发人员说的项目:)
TestNG支持Java中的并发testing。 本文介绍如何使用它,并在testng网站上有文档。
不知道你是否可以在同一时间做同样的testing
这里最好的方法是使用系统testing来通过请求来查看代码是否会崩溃。 然后使用unit testing来检查逻辑的正确性。 我会这样做的方式是创buildasynchronous调用的代理,并让它在testing中同步。
然而,如果你想在完全安装和完整的环境以外的级别上做到这一点,你可以通过在单独的线程中创build对象来实现这一点,然后创build大量的线程,将请求发送到对象并阻止主线程直到完成。 如果你不正确,这种方法会使testing失败。
你也可以试试HavaRunner 。 它默认并行运行testing。
你可以检查IMUnit 。 它与JUnit兼容。