TestTemplate注解


以下是关于 JUnit 5 @TestTemplate 注解的详细介绍,包括核心概念、使用场景、自定义扩展实现及完整示例:


一、@TestTemplate 是什么?

@TestTemplate 是 JUnit 5 中用于定义测试模板的注解。它本身不直接执行测试,而是通过关联的 TestTemplateInvocationContextProvider 扩展动态生成多个测试调用。每个调用可以有不同的参数、显示名称或自定义行为。
核心用途:支持高度灵活的测试生成逻辑(如参数化、重复测试等)。


二、@TestTemplate 与其他注解的关系

注解说明
@Test普通测试方法,仅执行一次。
@ParameterizedTest基于 @TestTemplate 实现,通过预定义的参数源生成测试用例。
@RepeatedTest基于 @TestTemplate 实现,重复执行相同逻辑多次。
@TestTemplate底层机制,允许自定义扩展生成测试用例(需实现 TestTemplateInvocationContextProvider)。

三、核心组件

  1. TestTemplateInvocationContextProvider
    接口,负责为 @TestTemplate 方法生成多个调用上下文(TestTemplateInvocationContext)。
  2. TestTemplateInvocationContext
    定义单个测试调用的上下文,包括参数、显示名称和扩展。

四、使用场景

  1. 自定义参数源
    从数据库、文件或 API 动态加载测试数据。
  2. 复杂测试生成逻辑
    例如根据条件组合生成不同的测试策略。
  3. 扩展 JUnit 功能
    实现第三方库或自定义测试模式(如行为驱动开发)。

五、基础示例:使用现有扩展(如 @ParameterizedTest

虽然 @ParameterizedTest 内部基于 @TestTemplate,但用户通常直接使用它:

@ParameterizedTest
@ValueSource(ints = {1, 2, 3})
void parameterizedTest(int value) {
    Assertions.assertTrue(value > 0);
}

六、自定义 @TestTemplate 扩展

1. 实现 TestTemplateInvocationContextProvider
import org.junit.jupiter.api.extension.*;

public class CustomTestTemplateProvider implements TestTemplateInvocationContextProvider {

    @Override
    public boolean supportsTestTemplate(ExtensionContext context) {
        return true; // 根据条件决定是否支持该测试方法
    }

    @Override
    public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
        ExtensionContext context
    ) {
        return Stream.of(
            // 生成两个调用上下文
            invocationContext("测试调用 1", "参数1"),
            invocationContext("测试调用 2", "参数2")
        );
    }

    private TestTemplateInvocationContext invocationContext(String name, String param) {
        return new TestTemplateInvocationContext() {
            @Override
            public String getDisplayName(int invocationIndex) {
                return name; // 自定义显示名称
            }

            @Override
            public List<Extension> getAdditionalExtensions() {
                return Collections.singletonList(new ParameterResolver() {
                    @Override
                    public boolean supportsParameter(
                        ParameterContext parameterContext, 
                        ExtensionContext extensionContext
                    ) {
                        return parameterContext.getParameter().getType() == String.class;
                    }

                    @Override
                    public Object resolveParameter(
                        ParameterContext parameterContext, 
                        ExtensionContext extensionContext
                    ) {
                        return param; // 注入参数
                    }
                });
            }
        };
    }
}
2. 使用 @TestTemplate 和自定义扩展
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith(CustomTestTemplateProvider.class)
class TestTemplateDemo {

    @TestTemplate
    void testTemplate(String input) {
        System.out.println("执行测试,输入参数: " + input);
        Assertions.assertNotNull(input);
    }
}
执行结果:

生成 测试调用 1和测试调用 2两个测试 用例,输入参数分别是参数1和参数2


七、高级用法:动态生成参数

结合外部数据源(如 CSV 文件)生成测试调用:

public class CsvTestTemplateProvider implements TestTemplateInvocationContextProvider {

    @Override
    public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
        ExtensionContext context
    ) {
        try {
            return Files.lines(Paths.get("src/test/resources/test-data.csv"))
                .map(line -> line.split(","))
                .map(columns -> createContext(columns));
        } catch (IOException e) {
            return Stream.empty();
        }
    }

    private TestTemplateInvocationContext createContext(String[] columns) {
        return new TestTemplateInvocationContext() {
            @Override
            public String getDisplayName(int invocationIndex) {
                return "CSV 测试行: " + invocationIndex;
            }

            @Override
            public List<Extension> getAdditionalExtensions() {
                return List.of(new ParameterResolver() {
                    @Override
                    public boolean supportsParameter(
                        ParameterContext parameterContext, 
                        ExtensionContext extensionContext
                    ) {
                        return parameterContext.getIndex() < columns.length;
                    }

                    @Override
                    public Object resolveParameter(
                        ParameterContext parameterContext, 
                        ExtensionContext extensionContext
                    ) {
                        int index = parameterContext.getIndex();
                        return columns[index];
                    }
                });
            }
        };
    }

    // 省略 supportsTestTemplate 方法
}

八、注意事项

  1. 扩展注册
    自定义的 TestTemplateInvocationContextProvider 需通过 @ExtendWith 注册,或通过 META-INF/services 自动加载。
  2. 参数解析
    使用 ParameterResolver 扩展为测试方法注入参数。
  3. 生命周期
    每次调用会触发 @BeforeEach@AfterEach,但 @BeforeAll@AfterAll 仅执行一次。

九、总结

何时使用 @TestTemplate

  • 需要完全控制测试用例的生成逻辑。
  • 现有注解(如 @ParameterizedTest)无法满足需求。
  • 实现自定义的测试模式或集成外部数据源。

优势
通过自定义 TestTemplateInvocationContextProvider,可以实现高度灵活的测试生成策略,适合复杂场景或框架扩展。对于简单需求,优先使用 @ParameterizedTest@RepeatedTest

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值