Parameterized JUnit tests and Spring contexts

SpringI like Spring. I really like using it and I use it quite often. One of its neat features is creating Spring managed contexts for JUnit test cases. Define your contexts either via XML or JavaConfig (I prefer the latter one).

Thanks to the SpringJUnit4Runner your JUnit tests easily benefit from DI features.

A common springified JUnit may look like this

@RunWith(SpringJUnit4Runner.class)
@ContextConfiguration(classes=TestClass.Context.class)
public class TestClass {

    @Configuration
    static class Context {
     // ...
     // your bean definitions
     //
    }

    // injected beans
    @Inject
    private SomeSpringManagedComponent myBean;
    ...
    ...
    @Test
    public void someTestMethod() {
      ...
    }
}

junit-logo@Parameterized – Scale your test case

On the other hand I like JUnit’s @Parameterized runner too and in many occasions it becomes very handy.
However, JUnit does not allow you to use multiple runners.

@Parameterized + TestContextManager will get the job done

But you are not lost here at all. We just dig a bit deeper into Spring’s test context framework and simply grab the TestContextManager for this purpose. Adding it to our JUnit’s enables our tests to run with multiple test data and still benefit from Spring’s magic.

Such a JUnit test may look like this

@RunWith(Parameterized.class)
@ContextConfiguration(classes = TestClass.Context.class)
public class TestClass { 

    @Parameterized.Parameters(name="{index} : {0}")
    public static Iterable<Object[]> parameters() {
        return Arrays.asList(new Object[][]{
                {"testCase1", new TestData("blah")}
                ,{"testCase2", new TestData("blah blah")}
        });
    };

    @Configuration
    static class Context {
     // ...
     // your bean definitions
     //
    }

    // injected beans
    @Inject
    private SomeSpringManagedComponent myBean;
    ...
    ...

    private TestData testData;

    public TestClass(final String testCase, final TestData td) {
        this.testData = td;
    }

    @Before
    public void setUp() throws Exception {
        mgr = new TestContextManager(this.getClass());
        mgr.prepareTestInstance(this);

    }

    @Test
    public void someTestMethod() {
      ...
    }
}

The test case is configured to run with the Parameterized runner enabling us to provide appropriate test data via an annotated static method (see @Parameterized.Parameters annotation). The previous Spring specific annotations and context definition remain.
Within the @Before annotated set up method we programmatically create a Spring context via the TestContextManager.
Basically we are done here and our spring managed beans do get injected into our test case.

Test isolation

A common good practice is to isolate your test cases properly. However, the above test case configuration would create a single Spring context shared across all parameterized test runs. Sharing a Spring test context may be subject to weird side effects and mixed states.
A slightly modified version just like the following one would achieve a far satisfying solution in terms of test case isolation.

@RunWith(Parameterized.class)
@ContextConfiguration(classes = TestClass.Context.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class TestClass {

    //...same as 1st version

    @After
    public void tearDown() throws Exception {
        mgr.afterTestClass();
    }

   //...same as 1st version
}

Additionally, we provide an @After method and tell the TestContextManager to run some clean up. Internally, all declared TestExecutionListeners get notified. Having annotated with @DirtiesContext will let the DirtiesContextTestExecutionListener participate in the test lifecyle such that the Spring test context is destroyed after each test case execution.

Spring 4.2, @Rule support via SpringClassRule and SpringMethodRule

If you are lucky enough to upgrade to Spring 4.2 you may have this feature now out-of-the-box.
SpringClassRule is a custom JUnit TestRule that supports class-level features of the Spring TestContext Framework in standard JUnit tests by means of the TestContextManager and associated support classes and annotations. SpringMethodRule is a custom JUnit MethodRule that supports instance-level and method-level features of the Spring TestContext Framework in standard JUnit tests by means of the TestContextManager and associated support classes and annotations.
In contrast to the SpringJUnit4ClassRunner, Spring’s rule-based JUnit support has the advantage that it is independent of any Runner and can therefore be combined with existing alternative runners like JUnit’s Parameterized or other third-party runners.