How to tell spring to only load the needed beans for the JUnit test?

26,537

Solution 1

It's not direct answer, so I'd would not mark as solution. But hope it's helpful.

Generally I see three options.

  1. As VinayVeluri answered nicely. Create separate contexts and launch them in every tests separately.

  2. Create context one time per all tests. Just like here: Reuse spring application context across junit test classes It's a big optimization for testing all tests at once.

  3. Mix those two first points. Create one smaller context only for testing purpose. Mock that, what's never is tested but can throw NPE etc. Like here: Injecting Mockito mocks into a Spring bean to boost up context build. And re-use it like in point 2. One time build for all tests. Personally I'd go with that one.

  4. This one waiting for answer about some kind of smart test runner, which creates minimum needed context per test.

Solution 2

Consider adding default-lazy-init="true" to your spring context xml beans tag (or add lazy-init="true" to those specific beans that take a long time starting up). This will ensure that only those beans are created that called with applicationContext.getBean(class-or-bean-name) or injected via @Autowired / @Inject into your tests. (Some other types of beans like @Scheduled beans will be created nevertheless but you need to check if that's a problem or not)

(if you use spring Java configuration, add @Lazy to the config files)

Caveat - If there is a bean that is not initialized explicitly with applicationContext.getBean() or injected as a dependency used by the bean obtained by using applicationContext.getBean(), then that bean will NO LONGER be constructed or initialized. Depending upon your application, that can cause things to fail OR not. Maybe you can selectively mark those beans as lazy-init="false"

Solution 3

Yes, we can do that, using context per test case. Prepare a test context xml file with the beans required for your test case.

If you use maven, place the test-context.xml under src/test/resources folder.

Annotate your required test class with the following annotation

@ContextConfiguration(locations = "classpath:test-application-context.xml")

This helps in loading only specific beans for the test case.

If you have two kinds of test cases, then

@Runwith(SpringJUnit4Runner.class)
@ContextConfiguration(locations = "classpath:test-context-case1.xml")
public class TestClassCase1 {}

@Runwith(SpringJUnit4Runner.class)
@ContextConfiguration(locations = "classpath:test-context-case2.xml")
public class TestClassCase2 {}
Share:
26,537
SandMan
Author by

SandMan

Updated on July 12, 2020

Comments

  • SandMan
    SandMan almost 4 years

    A simple question that might have an advanced answer.

    The Question: My question is, is there a way to instantiate only the classes, in your application context, needed for that specific JUnit test ?

    The Reason: My application context is getting quite big. I also do a lot of integration tests so you I guess you would understand when I say that every time I run a test all the classes in my application context get instantiated and this takes time.

    The Example:

    Say class Foo inject only bar

    public class Foo {
    
    @Inject
    Bar bar;
    
    @Test
    public void testrunSomeMethod() throws RegisterFault {
        bar.runSomeMethod();
    }
    

    but the application context has beans foobar and bar. I know this is not a vaild application context but rest assure all my code works.

    <beans>
         <bean id="foobar" class="some.package.FooBar"/>
         <bean id="bar" class="some.package.Bar"/>
    <beans>
    

    So how do I tell spring to only instantiate Bar and ignore FooBar for the test class foo.

    Thank you.