We have a Spring based JUnit test class which is utilizing an inner test context configuration class
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ServiceTest.Config.class)
public class ServiceTest {
@Test
public void someTest() {
...
@Configuration
@PropertySource(value = { "classpath:application.properties" })
@ComponentScan({ "..." })
public static class Config {
...
New functionalities have been recently introduced to the Service class, for which the concerned tests should be added to ServiceTest. However these would also require a different test context configuration class to be created (the internals of the existing Config class are fairly complex and change it to serve both old and new tests seems to be be extremely difficult if possible at all)
Is there a way to achieve that certain test methods in one test class would use one config class and other methods would use another? @ContextConfiguration
seems to be applicable only on class level, so solution could be to create another test class for the new tests which would utilize its own context configuration class; but it would mean that the same Service class is being covered via two different test classes
I use these approaches when I'm have to solve this:
- Manually build the context in a setup method instead of using annotations.
- Move the common test code to a base class and extend it. That allows me to run the tests with different spring contexts.
- A mix of the two above. The base class then contains methods to build spring contexts from fragments (which the extensions can override). That also allows me to override test cases which don't make sense or do extra pre/post work in some tests.
Keep in mind that annotations only solve generic cases. You'll have to replicate some or all of their work when you leave the common ground.
With Aaron's suggestion of manually building the context I couldn't find any good examples so after spending some time getting it working I thought I'd post a simple version of the code I used in case it helps anyone else:
class MyTest {
@Autowired
private SomeService service;
@Autowired
private ConfigurableApplicationContext applicationContext;
public void init(Class<?> testClass) throws Exception {
TestContextManager testContextManager = new TestContextManager(testClass);
testContextManager.prepareTestInstance(this);
}
@After
public void tearDown() throws Exception {
applicationContext.close();
}
@Test
public void test1() throws Exception {
init(ConfigATest.class);
service.doSomething();
// assert something
}
@Test
public void test2() throws Exception {
init(ConfigBTest.class);
service.doSomething();
// assert something
}
@ContextConfiguration(classes = {
ConfigATest.ConfigA.class
})
static class ConfigATest {
static class ConfigA {
@Bean
public SomeService someService() {
return new SomeService(new A());
}
}
}
@ContextConfiguration(classes = {
ConfigBTest.ConfigB.class
})
static class ConfigBTest {
static class ConfigB {
@Bean
public SomeService someService() {
return new SomeService(new B());
}
}
}
}