表 1. 扩展的 annotation 说明
名称 参数 使用对象 备注 ParallelSetting threadNum: 用于指定线程数目。通常我们希望能使用一个数组指定多个值,这样,我们可以了解程序是否在单线程,较少线程,以及大量线程的情况下是否工作正常。 用于整个 TestCase 来指定测试所用的并行设置 用于指定整个 TestCase 的并行设置 InitFor 指定此方法服务的测试方法 用于指定初始化方法所服务的并行测试方法 Threaded 无参数,或定义与 @Test 兼容的参数 用于一个具有两个 int 类型的测试方法。测试过程中,测试框架将会线程序号以及线程总数通过方法的参数传递进来。这有点类似于 MPI 的约定。 指明一个方法为并行测试方法,相当于 JUnit 原有的 @Test 注释 CheckFor 一个字符串参数,用于指定需要被验证的并行测试方法。 一个 Threaded 修饰的方法 指明一个方法用于检测并行执行的结果 . 我们不能在 Threaded 方法中直接检查,因为其他线程的测试也许还没有结束。
扩展 JUnit 的过程说明
归功于 JUnit 的灵活的内部架构,只要遵循 JUnit 的标准,我们就能够轻松的扩展 JUnit 的功能。而且遵循标准还意味着我们的扩展能无缝的利用社区对 JUnit 的广泛支持。例如,我们没有编写任何 Eclipse 插件,但是我们的测试结果能自然的在 Eclipse 中通过精心设计的 GUI 进行展现。
言归正传,我们扩展 JUnit 的过程主要由以下过程组成:
生成 Annotation 的定义,包括:@Threaded, @InitFor, @Check, @ParallelSetting
生成 TestClassRunner 的子类 Parallelized 并在其中实现运行自定义测试的逻辑
生成 TestMethodRunner 的子类供 Parallelized 类使用
在实现 ThreadedMethodRunner 时,我们最开始在类 ThreadedMethodRunner 使用了 Thread 类的 setDefaultUncaughtExceptionHandler 来捕获异常。然后将异常封装到主线程。而目前的版本则利用了 Executor 来运行多线程测试。由于 JDK 中的 Future 已经提供了类似的能力,所以我们不需要再关心异常的正确传递问题了。 JUnit 能准确的打印出并行测试中产生的异常信息,这也意味着我们可以使用 JUnit 提供的 Assert 功能了。
结论
随着多核平台逐渐成为主流,开发人员不可避免地需要开发和测试并行应用。本文通过使用 Annotation 扩展 JUnit,使其可以更方便地支持“准备数据——多线程运行——检查结果”三阶段的并行测试模式,减少开发人员手工创建线程和同步的繁琐工作。并且可以使 JUnit 支持从子线程中捕获测试错误,正确地在 Eclipse 等 IDE 中显示测试结果。