You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在@TestFactory的每个动态测试中创建独立的Spring上下文?

问题:动态生成Spring测试时如何初始化独立上下文

我正尝试在项目中实现基于CSV的测试,以便在测试文件夹中新增带输入数据的CSV文件后,maven会自动运行新测试。
目前我使用@SpringJUnitConfig,并通过@Primary注解覆盖项目配置,现有代码如下:它会基于测试配置仅启动一个测试

package com.bch.score.test.framework;
@SpringJUnitConfig({FrameworkSpringContext.class})
public class MySingleTestLauncher {
    @Autowired
    public MainApplicationService service;
    @Autowired
    public TestDbService dbPreparationService;
    @Autowired
    public IFrameworkDatabaseConfig dbSettings;
    @Autowired
    public TestOutputChecker outputChecker;

    @Test
    public void testSingleCase() {
        dbPreparationService.fillDatabase(dbSettings);
        service.launchApp();
        outputChecker.checkResult();
    }
}

我在FrameworkSpringContext类中声明了同时扫描应用包和测试包的规则:

@Configuration
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
@ComponentScan(value = {"com.bch.score.test.framework.config", "com.bch.score.cmd"})
public class FrameworkSpringContext {
    private final ISettingsProvider settings;

    @Autowired
    public FrameworkSpringContext(ISettingsProvider settings) {
        this.settings = settings;
    }

    @Bean
    public TestDbService service() {
        return new TestDbService();
    }

    @Bean
    public TestOutputChecker outputAssertion() {
        return new TestOutputChecker();
    }

    @Bean
    public IFrameworkDatabaseConfig dbSettings() {
        return new FrameworkDatabaseConfigImpl(settings.getUrl(), settings.getSchema(), settings.getLogin(), settings.getPassword());
    }
}

我还覆盖了应用内部使用的settings bean:

@PropertySource("classpath:score-framework.properties")
@Configuration
public class SettingsConfigurationFramework {
    @Primary
    @Bean(name = "testSettings")
    public ISettingsProvider settingsProvider(MessageSource messageSource, Environment environment) {
        return new SettingsProviderImpl(messageSource, environment);
    }
}

这套方案运行正常,但每新增一个测试都需要复制MySingleTestLauncher和配置类。我希望使用@TestFactory按文件夹生成测试,预期的测试启动类写法如下:

@SpringJUnitConfig({FrameworkSpringContext.class})
public class MultipleTestLauncher {
    @Autowired
    public TestDbService dbPreparationService;
    @Autowired
    public Function<String, IFrameworkDatabaseConfig> dbSettingsFactory;
    @Autowired
    public TestOutputChecker outputChecker;
    @TestFactory
    public Collection<DynamicTest> testTests() throws IOException {
        List<Path> subFolders = Files.list(Paths.get("src", "test", "resources", "framework", "tests"))
            .filter(path -> path.getFileName().toString().startsWith("test_"))
            .collect(Collectors.toList());
        List<DynamicTest> dynamicTests = new ArrayList<>();
        for (Path testPath : subFolders) {
        String currentTestFolder = testPath.getFileName().toString();
        dynamicTests.add(DynamicTest.dynamicTest(currentTestFolder, () -> {
            IFrameworkDatabaseConfig dbSettings = dbSettingsFactory.apply(currentTestFolder);
            dbPreparationService.fillDatabase(dbSettings);
            //TODO 以某种方式为每次应用启动创建新的Spring上下文...
            //TODO getTestContext().getBean(MainApplicationService.class).launchApp();
            outputChecker.checkResult();
        }));
    }
    }
}

每个测试都要类似一次全新的应用启动,我需要测试之间完全隔离,避免互相影响输出结果。如果每个测试都有独立的Spring上下文,我就可以基于测试配置轻松mock ISettingsProvider,实现不同的断言逻辑。
我的问题是:为动态生成的测试初始化Spring上下文的最佳实践是什么?
我是否需要参考Spring的上下文缓存机制?在我看来,直接创建新的应用上下文更合理,可以隔离每次测试的应用启动过程,因此缓存可能不是最优选择。


解答

你要的完全隔离场景直接手动创建独立上下文即可,不需要使用Spring Test默认的上下文缓存机制,实现步骤如下:

  1. 先调整配置类,支持根据当前测试路径动态生成对应ISettingsProvider
    把原来的固定配置抽成可传入测试路径的构造形式,示例如下:
public class TestSpecificSettingsProvider extends SettingsProviderImpl {
    public TestSpecificSettingsProvider(String testFolder, MessageSource messageSource, Environment environment) {
        super(messageSource, environment);
        // 这里可以加载对应test文件夹下的自定义配置、CSV参数等
    }
}
  1. 修改动态测试的执行逻辑,手动初始化上下文,补全你的TODO部分:
dynamicTests.add(DynamicTest.dynamicTest(currentTestFolder, () -> {
    IFrameworkDatabaseConfig dbSettings = dbSettingsFactory.apply(currentTestFolder);
    dbPreparationService.fillDatabase(dbSettings);
    
    // 手动创建独立上下文
    try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
        // 注册当前测试专属的Settings Bean
        context.registerBean(ISettingsProvider.class, () -> 
            new TestSpecificSettingsProvider(currentTestFolder, context.getBean(MessageSource.class), context.getEnvironment())
        );
        // 注册公共配置类
        context.register(FrameworkSpringContext.class, SettingsConfigurationFramework.class);
        // 刷新上下文完成初始化
        context.refresh();
        
        // 从当前独立上下文获取Bean执行业务逻辑
        context.getBean(MainApplicationService.class).launchApp();
    }
    
    outputChecker.checkResult();
}));
  1. 额外注意点:
  • 用try-with-resources包裹上下文初始化逻辑,测试执行完成后会自动关闭上下文,释放数据库连接、线程池等资源,不会出现泄漏
  • 每个测试的上下文完全独立,Bean不会互相污染,完全满足你隔离测试的需求
  • 如果后续测试数量上来启动变慢,再考虑给相同配置的测试加上下文缓存即可,当前阶段优先保证隔离性即可

内容的提问来源于stack exchange,提问作者Worm359

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 22:15:01