Reputation: 3209
I would like to create 2 test case with JUnit and Spring that requires both the same classpath resource batch-configuration.properties but content of this file differ depending on test.
Actually in my maven project, I create these file tree :
But how can I define my root classpath depending on my test case (files are loaded in 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
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 :
@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...
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.
Upvotes: 7
Views: 21208
Reputation: 7911
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.
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:
@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 Javadoc for @ProperySource
states:
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.
Create an ApplicationContextInitializer
for your tests to add some test properties with highest search priority that will always 'win':
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
:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { ExtractionBatchConfiguration.class },
initializers = MockApplicationContextInitializer.class)
public class ExtractBatchTestCase {
// ...
}
Upvotes: 1