探索JUnit4擴(kuò)展:擴(kuò)展Runner
在使用JUnit的過程中,大家可能會(huì)對(duì)JUnit進(jìn)行一些擴(kuò)展。本文中的示例為JUnit4定義了一個(gè)新的Annotation,并相應(yīng)地對(duì)已有的Runner進(jìn)行擴(kuò)展,使其能夠解析新引入的Annotation。(2011.12.25最后更新)本文臆造了一個(gè)示例,會(huì)在執(zhí)行單元測(cè)試方法之前,自動(dòng)地為單元測(cè)試方法打印日志。該示例會(huì)為JUnit定義一個(gè)新的Annotation用于指定要打印的日志內(nèi)容,并對(duì)JUnit默認(rèn)提供的Runner實(shí)現(xiàn)BlockJUnit4ClassRunner進(jìn)行擴(kuò)展,使其能夠識(shí)別這個(gè)新的Annotation。
1. 定義Annotation
TestLogger是一個(gè)作用于方法的Annotation,它只有一個(gè)屬性,用于指定日志的內(nèi)容,其代碼如下所示,
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface TestLogger {
public String log() default "";
}
@Retention(RetentionPolicy.RUNTIME)
public @interface TestLogger {
public String log() default "";
}
2. 擴(kuò)展Runner
JUnit提供了若干個(gè)Runner的實(shí)現(xiàn),如BlockJUnit4ClassRunner,Suite,其中BlockJUnit4ClassRunner用來執(zhí)行單個(gè)測(cè)試用例類。LoggedRunner將擴(kuò)展BlockJUnit4ClassRunner,覆寫其中的methodBlock()方法。新的methodBlock()方法會(huì)在一開始試圖獲取被執(zhí)行測(cè)試方法中的TestLogger Annotation,如果存在的話,就會(huì)打印出指定的日志,每行日志以當(dāng)時(shí)的執(zhí)行時(shí)間與完整方法名作為前綴。該類的代碼如下所示,
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);
}
}
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. 應(yīng)用程序
Calculator是一個(gè)簡單的應(yīng)用程序,其中定義了一個(gè)除法方法,代碼如下所示,
public class Calculator {
public int divide(int a, int b) {
return a / b;
}
}
public int divide(int a, int b) {
return a / b;
}
}
4. 單元測(cè)試程序
CalculatorTest是一個(gè)簡單的單元測(cè)試程序,它會(huì)使用兩種方式對(duì)Calculator中的divide()方法進(jìn)行單元測(cè)試。其代碼如下所示,
@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);
}
}
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作為測(cè)試執(zhí)行器(@RunWith(LoggedRunner.class));同時(shí),每個(gè)單元測(cè)試方法,simpleDivide()與dividedByZero(),都使用了Annotation TestLogger,為其指定日志內(nèi)容。當(dāng)執(zhí)行上述單元測(cè)試時(shí),會(huì)自動(dòng)地打印出如下形式的日志內(nèi)容:
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.
2011-12-13_23:48:38_218 test.CalculatorTest#dividedByZero: divided by zero, and an ArithmeticException thrown.
5. 小結(jié)
通過對(duì)BlockJUnit4ClassRunner的擴(kuò)展,可以讓JUnit在運(yùn)行測(cè)試用例時(shí)做一些額外的工作。但這種直接修改默認(rèn)Test Runner的方式并不被提倡,在下一篇文章中將會(huì)介紹使用Test Rule來達(dá)到相同的擴(kuò)展目的。
posted on 2011-12-14 00:01 John Jiang 閱讀(3048) 評(píng)論(4) 編輯 收藏 所屬分類: Java 、UnitTest 、JUnit 、原創(chuàng)