在使用JUnit的过程中,大家可能会对JUnit进行一些扩展。本文中的示例为JUnit4定义了一个新的Annotation,并相应地对已有的Runner进行扩展,使其能够解析新引入的Annotation。(2011.12.25最后更新)
本文臆造了一个示例,会在执行单元测试方法之前,自动地为单元测试方法打印日志。该示例会为JUnit定义一个新的Annotation用于指定要打印的日志内容,并对JUnit默认提供的Runner实现BlockJUnit4ClassRunner进行扩展,使其能够识别这个新的Annotation。
1. 定义Annotation
TestLogger是一个作用于方法的Annotation,它只有一个属性,用于指定日志的内容,其代码如下所示,
2. 扩展Runner
JUnit提供了若干个Runner的实现,如BlockJUnit4ClassRunner,Suite,其中BlockJUnit4ClassRunner用来执行单个测试用例类。LoggedRunner将扩展BlockJUnit4ClassRunner,覆写其中的methodBlock()方法。新的methodBlock()方法会在一开始试图获取被执行测试方法中的TestLogger Annotation,如果存在的话,就会打印出指定的日志,每行日志以当时的执行时间与完整方法名作为前缀。该类的代码如下所示,
3. 应用程序
Calculator是一个简单的应用程序,其中定义了一个除法方法,代码如下所示,
4. 单元测试程序
CalculatorTest是一个简单的单元测试程序,它会使用两种方式对Calculator中的divide()方法进行单元测试。其代码如下所示,
值得注意的是,CalculatorTest特别指定LoggedRunner作为测试执行器(@RunWith(LoggedRunner.class));同时,每个单元测试方法,simpleDivide()与dividedByZero(),都使用了Annotation TestLogger,为其指定日志内容。当执行上述单元测试时,会自动地打印出如下形式的日志内容:
本文臆造了一个示例,会在执行单元测试方法之前,自动地为单元测试方法打印日志。该示例会为JUnit定义一个新的Annotation用于指定要打印的日志内容,并对JUnit默认提供的Runner实现BlockJUnit4ClassRunner进行扩展,使其能够识别这个新的Annotation。
1. 定义Annotation
TestLogger是一个作用于方法的Annotation,它只有一个属性,用于指定日志的内容,其代码如下所示,
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface TestLogger {
public String log() default "";
}
@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);
}
}
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;
}
}
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);
}
}
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.
2011-12-13_23:48:38_218 test.CalculatorTest#dividedByZero: divided by zero, and an ArithmeticException thrown.
5. 小结
通过对BlockJUnit4ClassRunner的扩展,可以让JUnit在运行测试用例时做一些额外的工作。但这种直接修改默认Test Runner的方式并不被提倡,在下一篇文章中将会介绍使用Test Rule来达到相同的扩展目的。
相关推荐
为了在项目中使用JUnit,你需要将`junit-4.11.jar`添加到项目的类路径中,并在测试类上使用`@RunWith(JUnit4.class)`注解来指定使用JUnit 4作为测试运行器。然后,你可以创建测试方法,这些方法通常以`test`开头,并...
JUnit4源码的完整版本包含了整个框架的实现细节,对于理解其工作原理、学习测试驱动开发(TDD)以及进行自定义扩展非常有帮助。 1. **JUnit核心概念**: - **Test Case**:在JUnit4中,测试用例是通过继承`org....
10. **扩展性与兼容性**:JUnit4设计得非常开放,可以通过实现`Runner`接口来扩展其功能,例如SpringJUnit4ClassRunner可以结合Spring框架进行测试。同时,JUnit4与大多数IDE和构建工具(如Maven、Gradle)良好集成...
JUnit 支持自定义测试监听器,可以扩展测试过程的监控和报告,例如使用`TestListenerAdapter`。 七、注解的继承与覆盖 在JUnit中,测试类和测试方法可以继承和覆盖注解,提供更细粒度的控制。 八、注解的使用示例 ...
自定义Runner是JUnit强大的扩展点,开发者可以根据需求实现特定的测试逻辑,例如,测试性能、并发测试或者模拟网络环境等。理解Runner的工作原理对于提升测试的效率和质量至关重要。 总结来说,JUnit的Runner是执行...
JUnit4允许通过装饰器模式扩展其功能,如`Timeout`注解可以设置测试方法的超时时间,`@Ignore`注解可以临时忽略某个测试。 9. 测试监听器(Test Listeners): JUnit4支持注册监听器,它们可以在测试生命周期的不同...
4. **运行测试**:编译完成后,可以使用 JUnit 提供的 Test Runner 运行测试。JUnit 支持两种运行模式:图形界面(GUI)和命令行界面(CLI)。 - **图形界面**:使用 `junit.swingui.TestRunner` 来启动 GUI 测试。...
对于更复杂的测试场景,JUnit 4 提供了 `@RunWith` 注解,允许用户指定自定义的测试运行器(Runner)。测试运行器是一个类,负责执行测试,可以扩展 JUnit 的功能。例如,`Parameterized` 测试运行器可以支持参数化...
《JUnit4:Java单元测试框架详解》 JUnit4是一款广泛应用于Java编程领域的单元测试框架,它的出现极大地简化了测试代码的编写,提升了测试的效率。本文将深入探讨JUnit4的核心特性、使用方法以及如何将其应用到实际...
7. **扩展与定制**:JUnit允许开发者通过实现`TestRule`或`Runner`接口来自定义测试行为。例如,可以创建一个自定义的测试规则,用于记录测试时间或者捕获异常信息。 8. **持续集成**:JUnit与其他持续集成工具(如...
1. **自定义 JUnit 运行器支持**:允许开发人员创建自己的 JUnit 运行器类,这些类可以扩展 `org.junit.runner.Runner` 或其子类,如 `org.junit.platform.runner.JUnitPlatformRunner`。这样,开发者可以在 Eclipse...
JUnit提供测试运行器(Runner)来控制测试执行,如`@RunWith(Parameterized.class)`支持参数化测试。测试套件(Suite)允许将多个测试类组合在一起执行,使用`@Suite.SuiteClasses({TestClass1.class, TestClass2....
1. **自定义运行器**:创建继承自JUnit的`BlockJUnit4ClassRunner`或`Parameterized`的自定义运行器,可以覆盖或扩展其行为,比如在测试执行前后的操作。 2. **测试筛选**:实现`org.junit.runner.manipulation....
Junit4支持自定义运行器,通过扩展`BlockJUnit4ClassRunner`,可以改变测试的执行方式,如并行执行、测试筛选等。 10. **实战演练** 学习Junit4不仅需要理解理论,还需要通过实际项目进行练习。`studyJunit4`目录...
此外,JUnit4还支持使用`Runner`自定义测试执行流程。 ### 七、实战应用 在实际开发中,单元测试是保证代码质量的重要手段。通过Junit4进行单元测试,可以: - 发现并修复早期bug,提高软件稳定性。 - 提升代码...
《深入理解JUnit4:Java测试框架的基石》 JUnit4是Java编程领域中广泛使用的单元测试框架,它的出现极大地简化了测试代码的编写,提高了软件质量。本文将深入解析JUnit4的核心概念、主要功能以及如何在Eclipse环境...
4. **高扩展性:** 由于其源代码是开放的,开发者可以根据自己的需求对其进行定制或扩展。 #### 三、JUnit的核心组件 JUnit框架的核心组件包括以下几个关键类和接口: - **Assert类:** 用于验证某个条件是否满足...
这里的`SpringJUnit4ClassRunner`是JUnit的扩展,它会启动Spring容器并初始化测试类中的bean。 接着,我们可以使用`@Autowired`注解来自动注入需要的依赖。假设我们有一个`MyService`服务需要在测试中使用: ```...
《深入解析JUnit4:探索开源测试框架的精髓》 JUnit4是Java开发中广泛使用的单元测试框架,它的开源性质使得开发者能够深入理解其内部机制,从而更好地利用它进行测试驱动开发(TDD)。本篇文章将从`junit4-example...