如何在@TestFactory 的每个测试中创建不同的 Spring 上下文?

Posted

技术标签:

【中文标题】如何在@TestFactory 的每个测试中创建不同的 Spring 上下文?【英文标题】:How to create different Spring context in each test of @TestFactory? 【发布时间】:2021-09-16 10:13:57 【问题描述】:

我正在尝试为我的项目实施基于 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.class 中,我声明了应用程序包和测试包的组件扫描:

@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());
    

我还覆盖了在应用程序中使用的设置 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 somehow create new Spring context for each app launch...
            //TODO getTestContext().getBean(MainApplicationService.class).launchApp();
            outputChecker.checkResult();
        ));
    
    

每次测试都像是一个新的应用程序启动。我希望将它们分开,以确保测试不会混淆彼此的输出。 为每个测试使用不同的 spring 上下文,我可以轻松地根据测试设置模拟 ISettingsProvider,并实现不同的断言。 所以我的问题是:为动态创建的测试初始化​​ Spring 上下文的最佳做法是什么?我应该查看上下文缓存(下面的链接)吗?https://docs.spring.io/spring-framework/docs/4.2.0.RC2/spring-framework-reference/html/integration-testing.html#testcontext-ctx-management-caching 在我看来,创建新的应用程序上下文是正确的做法,隔离每个测试应用程序的启动,因此缓存可能不是这里的最佳选择。

【问题讨论】:

【参考方案1】:

@DirtiesContext 可以提供帮助 它表示关联的测试或类修改了 ApplicationContext。它告诉测试框架关闭并为以后的测试重新创建上下文。

【讨论】:

以上是关于如何在@TestFactory 的每个测试中创建不同的 Spring 上下文?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Pig 中创建不区分大小写的匹配?

如何在 laravel 路由中创建不包括某些 slug 的 slug 路由?

如何在 Django 中创建不区分大小写的数据库索引?

我可以同时运行@TestFactory生成的Junit动态测试吗?

如何在Controller中创建不可变的BusinessLayer而不更改其属性?

在 Java 中创建不依赖 if-else 的工厂方法