[转]探索JUnit4扩展:扩展Runner
????????转载自http://www.blogjava.net/jiangshachina/archive/2011/12/14/366289.html? ??????? 在使用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(8,?2);
????????Assert.assertTrue(value?==?4);
????}
????@Test(expected?=?ArithmeticException.class)
????@TestLogger(log?=?"divided?by?zero, and an ArithmeticException thrown.")
????public?void?dividedByZero()?{
????????calculator.divide(8,?0);
????}
}
值得注意的是,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来达到相同的扩展目的。
?