内容


扩展 JUnit 测试并行程序

通过新的 Annotation 测试多线程程序

Comments

测试并行程序与以往有什么不同 ?

随着多核的普及,并行程序的开发已经提上日程。相对串行程序而言,并行程序更有可能出错。一方面,并行程序的执行序列具有很强的随机性,线程交错执行的序列可能每次都不一样,而只要一个序列有问题,整个程序就是不正确的。另一方面,并行程序对大多数程序员来说,都是一个新的领域,经验相对较少,这是容易出错的另外一个因素。

既然如此,我们就更需要仔细的测试我们的并行程序和组件了。目前已经有一些 JUnit 扩展可以创建多个线程,同时运行多个测试用例,从而加快测试用例集的执行速度,如 p-unit 。但对于测试并行程序和组件,这些功能并不能满足所有的需求。因为开发人员通常希望可以精确地控制多个线程之间的同步。

与测试顺序程序相同,我们希望能在测试并行操作之前,首先准备一些测试数据。然后,启动多个线程测试执行不同的操作。最后,等待所有线程结束之后,检验结果的正确性。在第二阶段中,多个线程能以任意的次序交错执行。结果的正确性检查应与线程的执行测序无关。

标准 JUnit 只捕捉来自主线程的 Exception 。而其他线程中产生的 Exception 则会安静地被忽略掉,使得我们在子线程运行出错的情况下仍旧能得到“ Green Bar ”。这显然不是程序员喜欢的测试行为,我们希望测试结果能正确地反映所有线程的运行结果。

这种用于并行程序的测试模式会在测试并行程序时会不断的重复。如果开发人员每次都需要重复创建这些框架,不仅繁琐,而且容易引入错误。通过使用以下介绍的简单扩展,可以使并行程序的测试变得和顺序程序一样简单。这种扩展并不影响 JUnit 的其他特性以及各种 IDE 的 JUnit 插件的使用。

下载并使用扩展框架

首先,我们给出一个使用新扩展进行并行测试的例子。

例 1. 使用 JUnit 扩展进行并行测试
 /** 
 * @author Zhi Gan 
 * 
 */ 
 @RunWith(Parallelized.class) 
 @ParallelSetting(threadNumber = { 1, 2, 4, 8 }) 
 public class TestThreaded { 
	 Set<String> strSet; 

	 @Before 
	 public void setUp() { 
		 strSet = new LockFreeSet(); 
	 } 

	 @Test 
	 public void doNothing() { 

	 } 
	
    @InitFor("testThread") 
	 public void putSomeData(int size){ 
		 strSet.add("putSomeData"); 
	 } 

    @Threadedpublic void testThread(int rank, int size) { 
		 // every thread adds element to set 
		 strSet.add("abcde" + rank); 
	 } 

    @CheckFor("testThread") 
	 public void checkResult(int size) { 
		 assertEquals(size+1, strSet.size()); 
	 } 

	 public static void main(String[] args) { 
		 for (int i = 0; i < 10; i++) 
			 JUnitCore.runClasses(TestThreaded.class); 
	 } 
 }

如果我们在 Eclipse 中运行测试,那么测试完毕之后的 JUnit 视图如下所示:

图 1. 并行测试的完成结果
Sample figure containing an image
Sample figure containing an image

接下来,我们模拟子线程在运行时抛出异常。

例 2. 子线程运行时异常
 /** 
 * @author Zhi Gan 
 * 
 */ 
 @RunWith(Parallelized.class) 
 @ParallelSetting(threadNumber = { 1, 2, 4, 8 }) 
 public class TestThreaded { 
	 Set<String> strSet; 

	 @Before 
	 public void setUp() { 
		 strSet = new LockFreeSet(); 
	 } 

	 @Test 
	 public void doNothing() { 

	 } 
	
    @InitFor("testThread") 
	 public void putSomeData(int size){ 
		 strSet.add("putSomeData"); 
	 } 

    @Threadedpublic void testThread(int rank, int size) { 
		 // throw a runtime error in spawned thread 
		 throw new RuntimeError(); 
	 } 

    @CheckFor("testThread") 
	 public void checkResult(int size) { 
		 assertEquals(size+1, strSet.size()); 
	 } 

	 public static void main(String[] args) { 
		 for (int i = 0; i < 10; i++) 
			 JUnitCore.runClasses(TestThreaded.class); 
	 } 
 }

如果我们在 Eclipse 中运行测试,那么测试完毕之后的 JUnit 视图如下所示:

图 2. 并行测试的完成结果
Sample figure containing an image
Sample figure containing an image

从上图可以看出,我们的并行测试用例通过了测试,并且它们在使用不同线程运行时都能正常的工作。而串行的测试方法 (doNothing) 的执行结果则完全和之前一样工作正常。也就是说,串行和并行测试可以在一个测试类中同时出现。

Annotation 详细说明

表 1. 扩展的 annotation 说明
名称参数使用对象备注
ParallelSettingthreadNum: 用于指定线程数目。通常我们希望能使用一个数组指定多个值,这样,我们可以了解程序是否在单线程,较少线程,以及大量线程的情况下是否工作正常。用于整个 TestCase 来指定测试所用的并行设置用于指定整个 TestCase 的并行设置
InitFor指定此方法服务的测试方法用于指定初始化方法所服务的并行测试方法 
Threaded无参数,或定义与 @Test 兼容的参数用于一个具有两个 int 类型的测试方法。测试过程中,测试框架将会线程序号以及线程总数通过方法的参数传递进来。这有点类似于 MPI 的约定。指明一个方法为并行测试方法,相当于 JUnit 原有的 @Test 注释
CheckFor一个字符串参数,用于指定需要被验证的并行测试方法。一个 Threaded 修饰的方法指明一个方法用于检测并行执行的结果 . 我们不能在 Threaded 方法中直接检查,因为其他线程的测试也许还没有结束。

扩展 JUnit 的过程说明

归功于 JUnit 的灵活的内部架构,只要遵循 JUnit 的标准,我们就能够轻松的扩展 JUnit 的功能。而且遵循标准还意味着我们的扩展能无缝的利用社区对 JUnit 的广泛支持。例如,我们没有编写任何 Eclipse 插件,但是我们的测试结果能自然的在 Eclipse 中通过精心设计的 GUI 进行展现。

言归正传,我们扩展 JUnit 的过程主要由以下过程组成:

  1. 生成 Annotation 的定义,包括:@Threaded, @InitFor, @Check, @ParallelSetting
  2. 生成 TestClassRunner 的子类 Parallelized 并在其中实现运行自定义测试的逻辑
  3. 生成 TestMethodRunner 的子类供 Parallelized 类使用

在实现 ThreadedMethodRunner 时,我们最开始在类 ThreadedMethodRunner 使用了 Thread 类的 setDefaultUncaughtExceptionHandler 来捕获异常。然后将异常封装到主线程。而目前的版本则利用了 Executor 来运行多线程测试。由于 JDK 中的 Future 已经提供了类似的能力,所以我们不需要再关心异常的正确传递问题了。 JUnit 能准确的打印出并行测试中产生的异常信息,这也意味着我们可以使用 JUnit 提供的 Assert 功能了。

结论

随着多核平台逐渐成为主流,开发人员不可避免地需要开发和测试并行应用。本文通过使用 Annotation 扩展 JUnit,使其可以更方便地支持“准备数据——多线程运行——检查结果”三阶段的并行测试模式,减少开发人员手工创建线程和同步的繁琐工作。并且可以使 JUnit 支持从子线程中捕获测试错误,正确地在 Eclipse 等 IDE 中显示测试结果。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology, Open source
ArticleID=360231
ArticleTitle=扩展 JUnit 测试并行程序
publish-date=12192008