
JUnit 5 不支持在抽象方法上直接使用 @Test 注解,需通过模板方法模式:在抽象父类中定义带 @Test 的具体委托方法,调用抽象业务测试方法,子类仅需实现逻辑而无需重复添加注解。
junit 5 不支持在抽象方法上直接使用 `@test` 注解,需通过模板方法模式:在抽象父类中定义带 `@test` 的具体委托方法,调用抽象业务测试方法,子类仅需实现逻辑而无需重复添加注解。
在从 JUnit 4 迁移至 JUnit 5 的过程中,一个常见误区是认为 @Test 可以像 JUnit 4 那样直接标注在抽象方法上,并由子类重写后自动识别为测试用例。但 JUnit 5 的测试发现机制(基于反射扫描非抽象、非静态、非私有的 @Test 方法)明确要求:@Test 必须出现在可执行的具体方法上——抽象方法本身无法被调用,因此即使加了注解也不会被识别为有效测试。
✅ 正确实践:采用「模板方法模式(Template Method Pattern)」
在抽象基类中定义两个角色分离的方法:
- 抽象钩子方法(hook methods):如 testOk() 和 testKo(),无注解、无实现,仅声明契约;
- 具体测试方法(test delegates):如 junitTestOk(),添加 @Test,内部调用对应钩子方法。
示例代码如下:
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(classes = {TestContext.class})
public abstract class AbstractTest {
// 契约:子类必须实现具体测试逻辑
public abstract void testOk();
public abstract void testKo();
// JUnit 5 可识别的真实测试方法 —— 模板方法
@Test
public void junitTestOk() {
testOk(); // 委托给子类实现
}
@Test
public void junitTestKo() {
testKo(); // 委托给子类实现
}
}
子类只需专注业务逻辑,无需任何测试注解:
public class SomeTest extends AbstractTest {
@Override
public void testOk() {
// ✅ 断言成功场景,例如:
assertThat(service.process("valid")).isTrue();
}
@Override
public void testKo() {
// ✅ 断言失败场景,例如:
assertThatThrownBy(() -> service.process("invalid"))
.isInstanceOf(IllegalArgumentException.class);
}
}
⚠️ 注意事项:
- 确保抽象基类不被直接实例化运行(JUnit 不会执行抽象类的测试方法,但若误配置为测试类可能引发 InstantiationException);
- 若使用 Spring Test,@SpringBootTest 应保留在抽象类上(如示例),确保所有子类共享相同上下文配置;
- 方法命名建议区分职责:钩子方法(如 testOk)体现语义,委托方法(如 junitTestOk)体现框架绑定,避免混淆;
- 此模式天然支持 @BeforeEach/@AfterEach 等生命周期注解——它们可在抽象类中统一定义,对所有子类生效。
总结:JUnit 5 强调显式、可执行的测试契约。放弃“注解继承”的幻想,转而拥抱清晰的模板结构,不仅能解决测试发现问题,还能提升测试体系的可维护性与扩展性。










