@test 注解本身不执行测试,由junit运行器通过反射识别并调用;运行时扫描方法、isannotationpresent判断、runtime保留策略确保可读,再通过newinstance、setaccessible和invoke完成实例化与调用。

JUnit 中的 @Test 注解本身不直接执行测试,它只是一个标记;真正触发测试方法调用的是 JUnit 运行器(如 JUnitPlatform 或 BlockJUnit4ClassRunner)在运行时通过反射机制识别、筛选并调用这些被标记的方法。
Test 注解如何被识别
JUnit 在启动测试时会扫描测试类中的所有公共方法,借助 Java 反射 API(如 Class.getDeclaredMethods())获取全部方法对象。随后遍历每个 Method 实例,调用其 isAnnotationPresent(Test.class) 判断是否标注了 @Test。只有返回 true 的方法才会被纳入待执行队列。
- 注解必须保留在运行期(
@Retention(RetentionPolicy.RUNTIME)),否则反射无法读取 - JUnit 5 的
@Test位于org.junit.jupiter.api.Test,与 JUnit 4 的包路径不同,反射需按实际类加载 - 默认只识别 public 方法;JUnit 5 支持 package-private(默认访问级别)方法,前提是 JVM 允许(通过
setAccessible(true)绕过访问控制)
反射调用测试方法的关键步骤
识别出目标方法后,JUnit 使用反射完成实例创建和方法调用:
- 通过
Class.getDeclaredConstructor().newInstance()创建测试类新实例(每次测试方法独立实例化,保障隔离性) - 对目标
Method对象调用setAccessible(true),确保即使方法是 private 或 package-private 也能被调用(JUnit 5 默认启用,JUnit 4 要求 public) - 执行
method.invoke(instance, (Object[])null)触发实际测试逻辑 - 若方法声明了参数(如 JUnit 5 的
@ParameterizedTest或生命周期扩展),JUnit 会提前准备参数数组并传入invoke
异常与生命周期的反射介入点
反射不仅用于调用 @Test 方法,还支撑整个测试生命周期管理:
-
@BeforeEach/@AfterEach方法通过类似方式识别,并在每个@Test方法前后反射调用 - 异常处理依赖反射捕获:JUnit 将
invoke包裹在 try-catch 中,若抛出未声明的异常或与expected不符,则标记为失败 - 超时机制(如
@Timeout)通过反射获取方法引用,再用单独线程+中断机制监控执行时长
自定义运行器中的反射实践
开发者可编写自定义 TestRunner 或扩展 Extension(JUnit 5),主动利用反射增强行为:
- 读取方法上的自定义注解(如
@Retry(3)),在反射调用外层加重试逻辑 - 通过
Method.getParameters()分析参数类型,自动注入 Mock 对象或上下文数据 - 使用
Method.getAnnotation()提取元数据,动态决定是否跳过该测试(如@DisabledOnCI)
理解这套反射流程,有助于调试测试不执行、访问异常或参数注入失败等问题,也能更合理地设计可测试代码和扩展机制。









