John Jiang

a cup of Java, cheers!
https://github.com/johnshajiang/blog

   :: 首页 ::  :: 联系 :: 聚合  :: 管理 ::
  131 随笔 :: 1 文章 :: 530 评论 :: 0 Trackbacks
探索JUnit4扩展:扩展Runner
在使用JUnit的过程中,大家可能会对JUnit进行一些扩展。本文中的示例为JUnit4定义了一个新的Annotation,并相应地对已有的Runner进行扩展,使其能够解析新引入的Annotation。(2011.12.25最后更新)

本文臆造了一个示例,会在执行单元测试方法之前,自动地为单元测试方法打印日志。该示例会为JUnit定义一个新的Annotation用于指定要打印的日志内容,并对JUnit默认提供的Runner实现BlockJUnit4ClassRunner进行扩展,使其能够识别这个新的Annotation。

1. 定义Annotation
    TestLogger是一个作用于方法的Annotation,它只有一个属性,用于指定日志的内容,其代码如下所示,
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface TestLogger {
    
public String log() default "";
}

2. 扩展Runner
    JUnit提供了若干个Runner的实现,如BlockJUnit4ClassRunner,Suite,其中BlockJUnit4ClassRunner用来执行单个测试用例类。LoggedRunner将扩展BlockJUnit4ClassRunner,覆写其中的methodBlock()方法。新的methodBlock()方法会在一开始试图获取被执行测试方法中的TestLogger Annotation,如果存在的话,就会打印出指定的日志,每行日志以当时的执行时间与完整方法名作为前缀。该类的代码如下所示,
public class LoggedRunner extends BlockJUnit4ClassRunner {

    
private static final DateFormat format = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss_SSS");

    
public LoggedRunner(Class<?> klass) throws InitializationError {
        
super(klass);
    }

    @Override
    
protected Statement methodBlock(FrameworkMethod method) {
        Method classMethod 
= method.getMethod();
        TestLogger loggerAnnotation 
= classMethod.getAnnotation(TestLogger.class);
        
if (loggerAnnotation != null) {
            StringBuilder log 
= new StringBuilder(format.format(new Date()));
            log.append(
" ").append(classMethod.getDeclaringClass().getName())
                    .append(
"#").append(classMethod.getName()).append("")
                    .append(loggerAnnotation.log());
            System.out.println(log.toString());
        }

        
return super.methodBlock(method);
    }
}

3. 应用程序
    Calculator是一个简单的应用程序,其中定义了一个除法方法,代码如下所示,
public class Calculator {

    
public int divide(int a, int b) {
        
return a / b;
    }
}

4. 单元测试程序
    CalculatorTest是一个简单的单元测试程序,它会使用两种方式对Calculator中的divide()方法进行单元测试。其代码如下所示,
@RunWith(LoggedRunner.class)
public class CalculatorTest {

    
private static Calculator calculator = null;

    @BeforeClass
    
public static void createCalculator() {
        calculator 
= new Calculator();
    }

    @Test
    @TestLogger(log 
= "a simple division.")
    
public void simpleDivide() {
        
int value = calculator.divide(82);
        Assert.assertTrue(value 
== 4);
    }

    @Test(expected 
= ArithmeticException.class)
    @TestLogger(log 
= "divided by zero, and an ArithmeticException thrown.")
    
public void dividedByZero() {
        calculator.divide(
80);
    }
}

值得注意的是,CalculatorTest特别指定LoggedRunner作为测试执行器(@RunWith(LoggedRunner.class));同时,每个单元测试方法,simpleDivide()与dividedByZero(),都使用了Annotation TestLogger,为其指定日志内容。当执行上述单元测试时,会自动地打印出如下形式的日志内容:
2011-12-13_23:48:38_218 test.CalculatorTest#simpleDivide: a simple division
2011-12-13_23:48:38_218 test.CalculatorTest#dividedByZero: divided by zero, and an ArithmeticException thrown.

5. 小结
通过对BlockJUnit4ClassRunner的扩展,可以让JUnit在运行测试用例时做一些额外的工作。但这种直接修改默认Test Runner的方式并不被提倡,在下一篇文章中将会介绍使用Test Rule来达到相同的扩展目的。
 
posted on 2011-12-14 00:01 John Jiang 阅读(3035) 评论(4)  编辑  收藏 所属分类: JavaUnitTestJUnit原创

评论

# re: 初探JUnit4扩展(原)[未登录] 2011-12-15 09:58 bruce
你好,请问一下,如果我要拦截失败的异常,要重写BlockJUnit4ClassRunner哪个方法呢?  回复  更多评论
  

# re: 初探JUnit4扩展(原) 2011-12-15 21:58 Sha Jiang
@bruce
可以看看对TestRule/ExpectedException的使用  回复  更多评论
  

# re: 初探JUnit4扩展(原) 2011-12-20 19:57 来如风
请问,这个和我直接在方法里logger.info()有啥区别!!  回复  更多评论
  

# re: 初探JUnit4扩展(原) 2011-12-21 20:12 Sha Jiang
@来如风
你是指在单元测试方法中直接使用Logger工具?
可能是没什么区别,甚至于在测试方法中直接使用Logger会更好些。
但在此处,用这个例子只是为了说明一种扩展JUnit的方式罢了。  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: