使用GroboUtils进行jUnit的多线程测试
jUnit不支持多线程测试(具体表现为,在@Test标记的方法中启动多线程测试,这是多线程中的Assert方法失效
具体原因是jUnit执行器执行的时候,执行线程很快退出,在多线程中还没有调用Assert的时候主线程就退出了,
而且,貌似jUnit也不会检测其他线程中调用Assert方法,即使其他线程中Assert方法失败了,
总的测试结果往往还是显示成功),
需要借用GroboUtils(http://groboutils.sourceforge.net/downloads.html)
GroboUtils是一个工具集合,里面包含各种测试工具,这里使用的是该工具集中的jUnit扩展.
这里使用的是GroboUtils v5版本,下载 Complete package:GroboUtils-5.zip
解压后 使用 GroboUtils-5\lib\core\GroboTestingJUnit-1.2.1-core.jar 这个即可.
GroboUtils 工具集中的jUnit扩展 的文档页面:
http://groboutils.sourceforge.net/testing-junit/index.html
示例(来自: http://mushiqianmeng.blog.51cto.com/3970029/897786):
/**
* 多线程测试用例
*
* @author lihzh(One Coder)
* @date 2012-6-12 下午9:18:11
* @blog http://www.coderli.com
*/
@Test
public void MultiRequestsTest() {
// 构造一个Runner
TestRunnable runner = new TestRunnable() {
@Override
public void runTest() throws Throwable {
// 测试内容
// 可以在这里直接调用Assert的各种方法,因为TestRunnable类是继承自Assert的
}
};
int runnerCount = 100;
//Rnner数组,想当于并发多少个。
TestRunnable[] trs = new TestRunnable[runnerCount];
for (int i = 0; i < runnerCount; i++) {
trs[i] = runner;
}
// 用于执行多线程测试用例的Runner,将前面定义的单个Runner组成的数组传入
MultiThreadedTestRunner mttr = new MultiThreadedTestRunner(trs);
try {
// 开发并发执行数组里定义的内容
mttr.runTestRunnables();
} catch (Throwable e) {
e.printStackTrace();
}
}
备忘:
JUnit best practices
http://www.javaworld.com/article/2076265/testing-debugging/junit-best-practices.html?page=3
主要关注3个类:TestRunnable,TestMonitorRunnable,MultiThreadedTestRunner,全部来自包:
net.sourceforge.groboutils.junit.v1.MultiThreadedTestRunner.
(1) TestRunnable 抽象类,表示一个测试线程,实例需要实现该类的runTest()方法,在该方法中写自己用的测试代码.
该类继承了jUnit的junit.framework.Assert类,所以可以在TestRunnable中使用各种Assert方法
(可惜因为GroboUtils使用的jUnit版本较老,且久未更新,新版本的jUnit中已经不推荐使用这个类的方法了).
该类实现了Runnable,在run方法中调用抽象方法runTest().
Doc中介绍
public abstract class TestRunnable
extends junit.framework.Assert
implements java.lang.Runnable
Instances of this class only execute in the runTestRunnables method of the MultiThreadedTestRunner class.
TestCases should define inner classes as a subclass of this, implement the runTest() method,
and pass in the instantiated class as part of an array to the runTestRunnables method.
Call delay( long ) to easily include a waiting period.
This class allows for all assertions to be invoked,
so that subclasses can be static or defined outside a TestCase.
If an exception is thrown from the runTest() method,
then all other test threads will terminate due to the error.
The runTest() method needs to be responsive to InterruptedException, resulting from the
owning MultiThreadedTestRunner interrupting the thread in order to signal the early
termination of the threads. The InterruptedExceptions may be propigated outside the runTest()
implementation with no harmful effects. Note that this means that InterruptedExceptions
are part of the framework, and as such carry information that your runTest()
implementations cannot override; in other words, don't let your test
propigate an InterruptedException to indicate an error.
Tests which perform a set of monitoring checks on the object-under-test should
extend TestMonitorRunnable, since monitors run until told to stop.
The Thread.stop() command will be sent with a MultiThreadedTestRunner.TestDeathException.
(2) MultiThreadedTestRunner
这个类相当与一个ExecuteService,可以用来执行 TestRunnable,构造函数需要传入TestRunnable数组,
表示需要测试的线程.
调用MultiThreadedTestRunner.runTestRunnables() 方法启动测试线程,开始执行测试.
这个方法默认让测试线程TestRunnable的run方法最多运行1天,也可以调用
MultiThreadedTestRunner.runTestRunnables(long maxTime) 这个方法,然测试线程TestRunnable
最多执行 maxTime 毫秒.如果超过maxTime毫秒之后,TestRunnable还没有执行完毕,则TestRunnable
会被中断,并且MultiThreadedTestRunner 会抛出异常,
导致测试失败fail("Threads did not finish within " + maxTime + " milliseconds.").
每个TestRunnable中runTest需要能够控制自己在什么时间自己结束自己,精确控制测试时间,不要利用
上面的maxTime.
另外,有上面Doc文档中:
If an exception is thrown from the runTest() method,
then all other test threads will terminate due to the error.
可知,假如有一个TestRunnable的runTest()方法中抛出异常,会终止所有TestRunnable的运行,
并导致测试失败.
Doc:
java.lang.Object
extended bynet.sourceforge.groboutils.junit.v1.MultiThreadedTestRunner
public class MultiThreadedTestRunner
extends java.lang.Object
A framework which allows for an array of tests to be run asynchronously.
TestCases should reference this class in a test method.
Update for July 9, 2003: now, you can also register TestRunner instances
as monitors (request 771008); these run parallel with the standard
TestRunner instances, but they only quit when all of the standard
TestRunner instances end.
Fixed bugs 771000 and 771001: spawned threads are now Daemon threads,
and all "wild threads" (threads that just won't stop) are Thread.stop()ed.
All these changes have made this class rather fragile, as there are many
threaded timing issues to deal with. Expect future refactoring with backwards compatibility.
3.TestMonitorRunnable
表示监控线程,可以让每一个TestRunnable对应一个TestMonitorRunnable,在TestMonitorRunnable中监控
TestRunnable.TestMonitorRunnable是TestRunnable的子类,提供了一些方便使用的方法.
TestMonitorRunnable使用示例参考:http://blog.csdn.net/wodestudy/article/details/17119741
附 TestMonitorRunnable 源码:
public abstract class TestMonitorRunnable extends TestRunnable
{
public TestMonitorRunnable()
{
super(true);
}
public abstract void runMonitor()
throws Throwable;
public void runTest()
throws Throwable
{
for(; !isDone() && !Thread.interrupted(); yieldProcessing())
runMonitor();
runMonitor();
}
protected void yieldProcessing()
throws InterruptedException
{
Thread.yield();
}
}