@ExtendWith 是 JUnit 5 中用于扩展测试框架功能的核心注解,它替代了 JUnit 4 中的 @RunWith 注解,提供了更灵活、更强大的扩展机制。通过 @ExtendWith,可以在测试生命周期的不同阶段插入自定义逻辑,实现诸如依赖注入、测试前置条件验证、异常处理等功能。
核心作用
@ExtendWith 的主要作用是注册一个或多个扩展类(Extension),这些扩展类可以干预测试的执行流程。JUnit 5 的扩展模型基于事件驱动,允许扩展类在测试生命周期的关键节点(如测试类初始化、测试方法执行前后等)执行自定义逻辑。
使用方式
1. 基本语法
// 单个扩展
@ExtendWith(MyExtension.class)
class MyTest { ... }
// 多个扩展(数组形式)
@ExtendWith({ExtensionA.class, ExtensionB.class})
class MyTest { ... }
- 可以标注在测试类或测试方法上。
- 若标注在类上,该扩展对类中所有测试方法生效;若标注在方法上,仅对该方法生效。
2. 常见内置扩展
JUnit 5 及第三方库(如 Spring、Mockito)提供了许多现成的扩展,例如:
-
MockitoExtension:由 Mockito 提供,用于简化 Mock 对象的创建和注入。@ExtendWith(MockitoExtension.class) class UserServiceTest { @Mock // 自动创建 Mock 对象 private UserRepository userRepo; @InjectMocks // 自动注入依赖 private UserService userService; } -
SpringExtension:由 Spring 提供,用于集成 Spring 容器,支持@Autowired等注解。@ExtendWith(SpringExtension.class) @ContextConfiguration(classes = AppConfig.class) class SpringIntegrationTest { @Autowired private UserService userService; } -
ParameterResolver相关扩展:用于解析测试方法的参数(如@TestInfo、@TestReporter等内置参数)。
自定义扩展
若内置扩展无法满足需求,可以通过实现 JUnit 5 提供的扩展接口来自定义扩展。常用的扩展接口包括:
| 接口 | 作用场景 |
|---|---|
BeforeAllCallback |
在 @BeforeAll 执行前触发 |
AfterAllCallback |
在 @AfterAll 执行后触发 |
BeforeEachCallback |
在 @BeforeEach 执行前触发 |
AfterEachCallback |
在 @AfterEach 执行后触发 |
BeforeTestExecutionCallback |
在测试方法执行前触发(@BeforeEach 之后) |
AfterTestExecutionCallback |
在测试方法执行后触发(@AfterEach 之前) |
ParameterResolver |
解析测试方法的参数(如自定义参数注入) |
TestExecutionExceptionHandler |
处理测试方法抛出的异常 |
自定义扩展示例:计时扩展
下面是一个简单的扩展,用于记录测试方法的执行时间:
import org.junit.jupiter.api.extension.*;
import java.time.Duration;
import java.time.Instant;
// 实现测试执行前后的回调接口
public class TimingExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback {
private Instant start;
// 测试方法执行前记录开始时间
@Override
public void beforeTestExecution(ExtensionContext context) {
start = Instant.now();
}
// 测试方法执行后计算并打印耗时
@Override
public void afterTestExecution(ExtensionContext context) {
Duration duration = Duration.between(start, Instant.now());
String testName = context.getDisplayName(); // 获取测试方法名称
System.out.printf("测试 [%s] 执行耗时: %d 毫秒%n", testName, duration.toMillis());
}
}
使用自定义扩展:
@ExtendWith(TimingExtension.class) // 注册计时扩展
class MyTest {
@Test
void testMethod1() throws InterruptedException {
Thread.sleep(100); // 模拟耗时操作
}
@Test
void testMethod2() throws InterruptedException {
Thread.sleep(200);
}
}
执行结果:
测试 [testMethod1] 执行耗时: 102 毫秒
测试 [testMethod2] 执行耗时: 201 毫秒
与 JUnit 4 @RunWith 的区别
-
灵活性:
@ExtendWith支持同时注册多个扩展(数组形式),而@RunWith只能指定一个运行器(Runner)。 - 扩展性:JUnit 5 的扩展模型基于多个细粒度接口,扩展类可按需实现特定接口;而 JUnit 4 的 Runner 需重写大量方法,耦合度较高。
-
兼容性:
@ExtendWith可以嵌套使用(结合@Nested注解),而@RunWith不支持嵌套测试类。
总结
@ExtendWith 是 JUnit 5 扩展机制的入口,通过它可以:
- 集成第三方框架(如 Spring、Mockito)。
- 自定义测试行为(如计时、日志、权限验证等)。
- 灵活控制测试生命周期,实现复杂的测试场景。
掌握 @ExtendWith 的使用和扩展原理,能极大提升 JUnit 5 测试的灵活性和可维护性。







网友评论