Use different Spring test context configuration for different test methods

13,408

Solution 1

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.

Solution 2

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

    }
}
Share:
13,408
hammerfest
Author by

hammerfest

Updated on June 06, 2022

Comments

  • hammerfest
    hammerfest about 2 years

    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

  • pinkpanther
    pinkpanther almost 8 years
    But does the Autowired injection work in the base class if we setup manually inside?
  • Aaron Digulla
    Aaron Digulla almost 8 years
    @pinkpanther: You're confusing the test setup with the code under test. The test setup doesn't use @Autowired. I'm avoiding injection in tests when I can: It slows the tests down and makes it harder to see what is going on. When IDEs support "show me what's injected here when I run the test", I'll change my mind.