java 如何在单元测试中设置不同的类路径以使用 Spring 加载资源

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32187488/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 19:47:55  来源:igfitidea点击:

How to set different classpath in an unit test to load resource with Spring

javaspringunit-testingspring-bootspring-batch

提问by Aure77

I would like to create 2 test case with JUnit and Spring that requires both the same classpath resource batch-configuration.propertiesbut content of this file differ depending on test.

我想用 JUnit 和 Spring 创建 2 个测试用例,它们需要相同的类路径资源batch-configuration.properties但此文件的内容因测试而异。

Actually in my maven project, I create these file tree :

实际上在我的 Maven 项目中,我创建了这些文件树:

  • src/test/resources/test1/batch-configuration.properties
  • src/test/resources/test2/batch-configuration.properties
  • src/test/resources/test1/batch-configuration.properties
  • src/test/resources/test2/batch-configuration.properties

But how can I define my root classpath depending on my test case (files are loaded in ExtractionBatchConfigurationusing classpath:batch-configuration.properties)

但是如何根据我的测试用例定义我的根类路径(文件是在ExtractionBatchConfigurationusing中加载的classpath:batch-configuration.properties

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { ExtractionBatchConfiguration.class }, loader = AnnotationConfigContextLoader.class)
@PropertySource("classpath:test1/batch-configuration.properties") // does not override ExtractionBatchConfiguration declaration
public class ExtractBatchTestCase {
    private static ConfigurableApplicationContext context;

    private JobLauncherTestUtils jobLauncherTestUtils;

    @BeforeClass
    public static void beforeClass() {
        context = SpringApplication.run(ExtractionBatchConfiguration.class);
    }

    @Before
    public void setup() throws Exception {      
        jobLauncherTestUtils = new JobLauncherTestUtils();
        jobLauncherTestUtils.setJobLauncher(context.getBean(JobLauncher.class));
        jobLauncherTestUtils.setJobRepository(context.getBean(JobRepository.class));
    }

    @Test
    public void testGeneratedFiles() throws Exception {     
        jobLauncherTestUtils.setJob(context.getBean("extractJob1", Job.class));
        JobExecution jobExecution = jobLauncherTestUtils.launchJob();
        Assert.assertNotNull(jobExecution);
        Assert.assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
        Assert.assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
        // ... other assert
    }

}

Config :

配置:

@Configuration
@EnableAutoConfiguration
@PropertySources({
    @PropertySource("batch-default-configuration.properties"),
    @PropertySource("batch-configuration.properties")
})
public class ExtractionBatchConfiguration { /* ... */ }

I am using Spring 4.0.9 (I cannot use 4.1.x) and JUnit 4.11

我使用的是 Spring 4.0.9(我不能使用 4.1.x)和 JUnit 4.11

EDIT:

编辑:

After using custom ApplicationContextInitializer suggested by hzpz to override my properties locations (application.properties + batch-configuration.properties) that solve some problems, I am experiencing another problem with @ConfigurationProperties :

使用 hzpz 建议的自定义 ApplicationContextInitializer 覆盖解决一些问题的属性位置(application.properties + batch-configuration.properties)后,我遇到了 @ConfigurationProperties 的另一个问题:

@ConfigurationProperties(prefix = "spring.ldap.contextsource"/*, locations = "application.properties"*/) 
public class LdapSourceProperties { 
    String url;
    String userDn;
    String password;
    /* getters, setters */
}

and configuration :

和配置:

@Configuration
@EnableConfigurationProperties(LdapSourceProperties.class)
public class LdapConfiguration {
    @Bean
    public ContextSource contextSource(LdapSourceProperties properties) {
        LdapContextSource contextSource = new LdapContextSource();
        contextSource.setUrl(properties.getUrl());
        contextSource.setUserDn(properties.getUserDn());
        contextSource.setPassword(properties.getPassword());
        return contextSource;
    }
}

All LdapSourceProperties's field are null when ContextSource is created but if I uncomment locations = "application.properties"it's only works if application.properties is in the root classpath. The default environment used by @ConfigurationProperties seems doesn't contains neested properties...

创建 ContextSource 时,所有 LdapSourceProperties 的字段都为空,但如果我取消注释,locations = "application.properties"它仅在 application.properties 位于根类路径中时才有效。@ConfigurationProperties 使用的默认环境似乎不包含需要的属性...

Alternative solution :

替代解决方案:

Finally I put all my properties into application-<profile>.propertiesfiles (and remove @PropertySource definition). I can now use application-test1.propertiesand application-test2.properties. On my test class, I can set @ActiveProfiles("test1")to activate a profile and load associated properties.

最后,我将所有属性放入application-<profile>.properties文件中(并删除 @PropertySource 定义)。我现在可以使用application-test1.propertiesapplication-test2.properties。在我的测试类中,我可以设置@ActiveProfiles("test1")激活配置文件并加载关联的属性。

采纳答案by hzpz

First of all you need to understand how JUnit tests with Spring work. The purpose of SpringJUnit4ClassRunneris to create the ApplicationContextfor you (using @ContextConfiguration). You do not need to create the context yourself.

首先,您需要了解使用 Spring 进行 JUnit 测试是如何工作的。的目的SpringJUnit4ClassRunnerApplicationContext为您创建(使用@ContextConfiguration)。您不需要自己创建上下文。

If the context is properly set up, you may then use @Autowiredto get the dependencies you need in your test. ExtractBatchTestCaseshould look something like this:

如果上下文设置正确,则您可以使用它@Autowired来获取测试中所需的依赖项。ExtractBatchTestCase应该是这样的:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { ExtractionBatchConfiguration.class })
public class ExtractBatchTestCase {

    @Autowired
    private JobLauncher jobLauncher;

    @Autowired
    private JobRepository jobRepository;

    @Autowired
    @Qualifier("extractJob1")
    private Job job;

    private JobLauncherTestUtils jobLauncherTestUtils;

    @Before
    public void setup() throws Exception {      
        jobLauncherTestUtils = new JobLauncherTestUtils();
        jobLauncherTestUtils.setJobLauncher(jobLauncher);
        jobLauncherTestUtils.setJobRepository(jobRepository);
    }

    @Test
    public void testGeneratedFiles() throws Exception {     
        jobLauncherTestUtils.setJob(job);
        JobExecution jobExecution = jobLauncherTestUtils.launchJob();
        Assert.assertNotNull(jobExecution);
        Assert.assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
        Assert.assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
        // ... other assert
    }

}

Second, the Javadocfor @ProperySourcestates:

其次,Javadocfor@ProperySource状态:

In cases where a given property key exists in more than one .properties file, the last @PropertySourceannotation processed will 'win' and override. [...]

In certain situations, it may not be possible or practical to tightly control property source ordering when using @ProperySourceannotations. For example, if the @Configurationclasses [...] were registered via component-scanning, the ordering is difficult to predict. In such cases - and if overriding is important - it is recommended that the user fall back to using the programmatic PropertySource API.

如果给定的属性键存在于多个 .properties 文件中,则@PropertySource处理的最后一个注释将“获胜”并覆盖。[...]

在某些情况下,在使用@ProperySource注释时严格控制属性源排序可能是不可能或不切实际的。例如,如果@Configuration类 [...] 是通过组件扫描注册的,则排序很难预测。在这种情况下 - 如果覆盖很重要 - 建议用户回退到使用编程 PropertySource API。

Create an ApplicationContextInitializerfor your tests to add some test properties with highest search priority that will always 'win':

ApplicationContextInitializer为您的测试创建一个以添加一些具有最高搜索优先级的测试属性,这些属性将始终“获胜”:

public class MockApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        MutablePropertySources propertySources = applicationContext.getEnvironment().getPropertySources();
        MockPropertySource mockEnvVars = new MockPropertySource().withProperty("foo", "bar");
        propertySources.addFirst(mockEnvVars);
    }
}

Declare it using @ContextConfiguration:

声明它使用@ContextConfiguration

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { ExtractionBatchConfiguration.class }, 
                      initializers = MockApplicationContextInitializer.class)
public class ExtractBatchTestCase {
    // ...
}