JUnit custom runner with Spring application context

DrewCo picture DrewCo · Apr 20, 2011 · Viewed 27.9k times · Source

I am fairly new to Spring and am working with a suite of JUnit 4.7 integration tests for a web application. I have a number of working test cases of the form:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/META-INF/spring/testContext.xml" })

public class myTest {
    @Test
    public void testCreate() {
            //execute tests
            ....
    }
}

My application has a number of external dependencies that I am testing, all of which have beans that are initialized through the loading of testContext.xml. Some of these external dependencies require custom code to initialize and tear down the necessary resources.

Rather than duplicate this code in every test class that requires it, I would like to encapsulate it into a common location. My thought was to create a seperate context definition as well as a custom runner that extends SpringJUnit4ClassRunner and contains the @ContextConfiguration annotation and related custom code, like so:

import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

//load the context applicable to this runner
@ContextConfiguration(locations = { "/META-INF/spring/testContext.xml" })

public class MyCustomRunner extends SpringJUnit4ClassRunner {

    public MyCustomRunner(Class<?> clazz) throws InitializationError {
        super(clazz);           
    }

    @Override
    protected Statement withBeforeClasses(Statement statement) {
        // custom initialization code for resources loaded by testContext.xml
                ...

        return super.withBeforeClasses(statement);
    }

    @Override
    protected Statement withAfterClasses(Statement statement) {
        // custom cleanup code for resources loaded by testContext.xml
                ....

        return super.withAfterClasses(statement);
    }

}

I could then have each test class specify its applicable runner with:

@RunWith(MyCustomRunner)

When I do this, my tests run and the proper withBeforeClasses and withAfterClasses methods are executed. However, no applicationContext is provided back to the test class and all of my tests fail with:

java.lang.IllegalArgumentException: Can not load an ApplicationContext with a NULL 'contextLoader'. Consider annotating your test class with @ContextConfiguration.

The context only loads correctly if I specify the @ContextConfiguration annotation on each test class - ideally, I would like this annotation to live with the handler code for the resources it is responsible for loading. Which leads me to my question - is it possible to load Spring context information from a custom runner class?

Answer

axtavt picture axtavt · Apr 20, 2011

You can create a base test class - @ContextConfiguration can be inherited, as well as @Before, @After, and so on:

@ContextConfiguration(locations = { "/META-INF/spring/testContext.xml" }) 
public abstract class myBaseTest { 
    @Before
    public void init() {
        // custom initialization code for resources loaded by testContext.xml 
    }

    @After
    public void cleanup() {
        // custom cleanup code for resources loaded by testContext.xml
    }
}

@RunWith(SpringJUnit4ClassRunner.class)
public class myTest extends myBaseTest { ... }