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
How to set different classpath in an unit test to load resource with Spring
提问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 ExtractionBatchConfiguration
using classpath:batch-configuration.properties
)
但是如何根据我的测试用例定义我的根类路径(文件是在ExtractionBatchConfiguration
using中加载的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>.properties
files (and remove @PropertySource definition). I can now use application-test1.properties
and 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.properties
和application-test2.properties
。在我的测试类中,我可以设置@ActiveProfiles("test1")
激活配置文件并加载关联的属性。
采纳答案by hzpz
First of all you need to understand how JUnit tests with Spring work. The purpose of SpringJUnit4ClassRunner
is to create the ApplicationContext
for you (using @ContextConfiguration
). You do not need to create the context yourself.
首先,您需要了解使用 Spring 进行 JUnit 测试是如何工作的。的目的SpringJUnit4ClassRunner
是ApplicationContext
为您创建(使用@ContextConfiguration
)。您不需要自己创建上下文。
If the context is properly set up, you may then use @Autowired
to get the dependencies you need in your test. ExtractBatchTestCase
should 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 @ProperySource
states:
其次,Javadocfor@ProperySource
状态:
In cases where a given property key exists in more than one .properties file, the last
@PropertySource
annotation processed will 'win' and override. [...]In certain situations, it may not be possible or practical to tightly control property source ordering when using
@ProperySource
annotations. For example, if the@Configuration
classes [...] 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 ApplicationContextInitializer
for 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 {
// ...
}