Runner from JUnit 4 in Spring

Spring TestContext Framework offers full integration with JUnit 4 through a dedicated runner (supported on JUnit 4.12 or higher). By marking test classes with the @RunWith(SpringJUnit4ClassRunner.class) annotation, or the shorter @RunWith(SpringRunner.class) annotation, developers can implement standard unit and integration tests based on JUnit 4 while taking advantage of the benefits of the TestContext framework, such as support for loading application contexts, dependency injection into test instances, transactional execution of test methods, and so on. If you need to use the Spring TestContext Framework with an alternative runner (such as the Parameterized runner from JUnit 4) or third-party runners (such as MockitoJUnitRunner), you can optionally , use Spring's JUnit rules support facilities instead.

The following code listing shows the minimum requirements to configure a test class to run using a custom Runner in Spring:

Java

@RunWith(SpringRunner.class)
@TestExecutionListeners({})
public class SimpleTest {
    @Test
    public void testMethod() {
        // testing logic...
    }
}
Kotlin

@RunWith(SpringRunner::class)
@TestExecutionListeners
class SimpleTest {
    @Test
    fun testMethod() {
        // testing logic...
    }
}

In the previous example, the @TestExecutionListeners annotation is configured with an empty list to disable listeners by defaults that would otherwise require configuring the ApplicationContext via the @ContextConfiguration annotation.

Spring JUnit 4 rules

Package org.springframework.test.context.junit4.rules provides the following JUnit 4 rules (supported on JUnit 4.12 or higher):

  • SpringClassRule

  • SpringMethodRule

SpringClassRule is JUnit's TestRule that supports class-level Spring TestContext Framework features, and SpringMethodRule is JUnit's MethodRule that supports instance- and method-level features of the Spring TestContext Framework.

Unlike SpringRunner, the advantage of Spring's rule-based JUnit supporter is that it is independent of any implementation org.junit.runner.Runner and can therefore be combined with existing alternative runners (such as Parameterized from JUnit 4) or third-party runners (such as MockitoJUnitRunner).

To support the full functionality of the TestContext framework, you need to combine SpringClassRule with SpringMethodRule. The following example shows the correct way to declare these rules in an integration test:

Java

// Optionally specify a runner other than Spring's Runner via @RunWith(. ..)
@ContextConfiguration
public class IntegrationTest {
    @ClassRule
    public static final SpringClassRule springClassRule = new SpringClassRule();
    @Rule
    public final SpringMethodRule springMethodRule = new SpringMethodRule();
    @Test
    public void testMethod() {
        // testing logic...
    }
}
Kotlin

// Optionally, we set a runner that is not a Spring Runner via @RunWith(...)
@ContextConfiguration
class IntegrationTest {
    @Rule
    val springMethodRule = SpringMethodRule()
    @Test
    fun testMethod() {
        // testing logic...
    }
    companion object {
        @ClassRule
        val springClassRule = SpringClassRule()
    }
}

JUnit 4 Helper Classes

The org.springframework.test.context.junit4 package provides the following helper classes for JUnit 4 based test cases (supported on JUnit 4.12 or higher):

  • AbstractJUnit4SpringContextTests

  • AbstractTransactionalJUnit4SpringContextTests

AbstractJUnit4SpringContextTests is an abstract base test class that integrates the Spring TestContext Framework with explicit ApplicationContext testing support into the JUnit 4 framework. If you extend AbstractJUnit4SpringContextTests, you can access the protected instance variable of the applicationContext, which can be used to perform explicit bean lookups or to test the state of the context as a whole.

AbstractTransactionalJUnit4SpringContextTests is an abstract transactional extension to AbstractJUnit4SpringContextTests that adds some convenience features for accessing JDBC. This class expects the ApplicationContext to have a javax.sql.DataSource bean and a PlatformTransactionManager bean defined. If you extend AbstractTransactionalJUnit4SpringContextTests, you can access the protected instance variable jdbcTemplate, which you can use to run SQL statements to query the database data. You can use such queries to validate the state of the database both before and after executing application code associated with the database, and Spring is guaranteed to ensure that such queries are executed within the same transaction as the application code. When used in conjunction with an ORM tool, false positives should be avoided. As mentioned in the JDBC Test Support section, AbstractTransactionalJUnit4SpringContextTests also provides helper methods that delegate authority to methods in JdbcTestUtils using the aforementioned jdbcTemplate. Moreover, AbstractTransactionalJUnit4SpringContextTests provides the executeSqlScript(..) method for executing SQL scripts against the configured DataSource.

These classes are extension helpers. If you don't need your test classes to be tied to a Spring-specific class hierarchy, you can set up your own custom test classes using the @RunWith(SpringRunner.class) annotation or JUnit rules for Spring.

SpringExtension for JUnit Jupiter

Spring TestContext Framework offers Full integration with the JUnit Jupiter test framework introduced in JUnit 5. By annotating test classes with @ExtendWith(SpringExtension.class), you can implement standard unit and integration tests on top of JUnit Jupiter while taking advantage of the framework's benefits TestContext, such as support for loading application contexts, dependency injection into test instances, transactional execution of test methods, and so on.

Moreover, thanks to the rich extension API in JUnit Jupiter, Spring provides the following capabilities on top of that set features that Spring supports for JUnit 4 and TestNG:

  • Dependency injection for test constructors, test methods, and test lifecycle callback methods. See Dependency Injection with SpringExtension for more information.

  • Full-featured support for conditional execution of tests based on SpEL expressions, environment variables, system properties, etc. For more information and examples, see the documentation for the @EnabledIf and @DisabledIf annotations in the "Annotations from JUnit Jupiter for testing in Spring".

  • Special compound annotations that combine annotations from Spring and JUnit Jupiter. For more information, see the @TransactionalDevTestConfig and @TransactionalIntegrationTest annotation examples in the "Support for meta annotations for testing".

The following code listing shows how to configure a test class to use SpringExtension in combination with @ContextConfiguration:

Java

// We command JUnit Jupiter to extend the test with support from Spring.
@ExtendWith(SpringExtension.class)
// Instruct Spring to load the ApplicationContext from TestConfig.class
@ContextConfiguration(classes = TestConfig.class)
class SimpleTests {
    @Test void testMethod() {
        // testing logic...
    }
} 
Kotlin

// We command JUnit Jupiter to extend the test with support from Spring.
@ExtendWith(SpringExtension::class)
// Instruct Spring to load the ApplicationContext from TestConfig.class
@ContextConfiguration(classes = [TestConfig::class])
class SimpleTests {
    @Test
    fun testMethod() {
        // testing logic...
    }
} 

Since JUnit 5 can also use annotations as meta annotations, Spring provides composite annotations @SpringJUnitConfig and @SpringJUnitWebConfig to simplify the configuration of the test ApplicationContext and JUnit Jupiter.

The following example uses the @SpringJUnitConfig annotation to reduce the amount of configuration used in previous example:

Java

// Instructs Spring to register a SpringExtension with JUnit
// Jupiter and load the ApplicationContext from TestConfig.class
@SpringJUnitConfig(TestConfig.class)
class SimpleTests {
    @Test
    void testMethod() {
        // testing logic...
    }
}
Kotlin

// Instructs Spring to register a SpringExtension with JUnit
// Jupiter and load the ApplicationContext from TestConfig.class
@SpringJUnitConfig(TestConfig::class)
class SimpleTests {
    @Test
    fun testMethod() {
        // testing logic...
    }
}

Similarly, the following example uses the @SpringJUnitWebConfig annotation to create a WebApplicationContext for use with JUnit Jupiter:

Java

// Instructs Spring to register a SpringExtension with JUnit
// Jupiter and load the WebApplicationContext from TestWebConfig.class
@SpringJUnitWebConfig(TestWebConfig.class)
class SimpleWebTests {
    @Test
    void testMethod() {
        // testing logic...
    }
}
Kotlin

// Instructs Spring to register a SpringExtension with JUnit
// Jupiter and load WebApplicationContext from TestWebConfig::class
@SpringJUnitWebConfig(TestWebConfig::class)
class SimpleWebTests {
    @Test
    fun testMethod() {
        // testing logic...
    }
}

For more information, see the documentation for @SpringJUnitConfig and @SpringJUnitWebConfig in the "Annotations from JUnit Jupiter for testing in Spring".

Dependency injection using SpringExtension

SpringExtension implements the extension API ParameterResolver from JUnit Jupiter, which allows Spring to provide dependency injection for test constructors, test methods, and test lifecycle callback methods.

In particular, SpringExtension can inject dependencies from an ApplicationContext test into test constructors and methods annotated with @BeforeAll, @AfterAll, @BeforeEach, @AfterEach, @Test, @RepeatedTest, @ParameterizedTest and others annotations.

Dependency injection via constructor

If a certain parameter in a test class constructor from JUnit Jupiter is of type ApplicationContext (or a subtype thereof) or annotated or meta-annotated with @Autowired, @Qualifier, or @Value, Spring injects the value of this parameter using the corresponding bean or value from the ApplicationContext test.

Spring can also be configured to automatically detect and bind all arguments to a test class's constructor if the constructor is considered auto-bindable. A constructor is considered autowired if one of the following conditions is true (in order of precedence).

  • The constructor is annotated with @Autowired.

  • The @TestConstructor annotation is present or meta-present for a test class with the autowireMode attribute set to ALL.

  • The default test constructor detection and binding mode has been changed to ALL.

More about using the @TestConstructor annotation and To change the automatic detection and binding mode of the global Test Constructor, see the section on the @TestConstructor annotation.

If the constructor for a test class is considered auto-bindable, Spring takes care of argument resolution for all parameters in the constructor. Therefore, no other ParameterResolver registered with JUnit Jupiter can resolve parameters for such a constructor.

Injection constructor dependencies for test classes cannot be used in conjunction with JUnit Jupiter's @TestInstance(PER_CLASS) annotation support if the @DirtiesContext annotation is used to close the ApplicationContext test before or after the test methods.

The reason is that the @TestInstance(PER_CLASS) annotation tells JUnit Jupiter to cache the test instance between test method calls. Therefore, the test instance will retain references to beans originally injected from an ApplicationContext that was subsequently closed. Because the constructor for the test class will only be called once in such scenarios, dependency injection will not be repeated, and subsequent tests will interact with beans from the private ApplicationContext, which may result in errors.

To use the @DirtiesContext annotation in "before test method" or "after test method" mode in combination with the @TestInstance(PER_CLASS) annotation, you need to configure Spring dependencies to receive them by injecting via a field or setter so that they can be reinjected between test method calls.

In the following example, Spring injects the OrderService bean from the ApplicationContext loaded from TestConfig.class into the OrderServiceIntegrationTests constructor.

Java

@SpringJUnitConfig(TestConfig .class)
class OrderServiceIntegrationTests {
    private final OrderService orderService;
    @Autowired OrderServiceIntegrationTests(OrderService orderService) {
        this.orderService = orderService;
    }
    // tests using the embedded OrderService
}
Kotlin

@SpringJUnitConfig(TestConfig::class)
class OrderServiceIntegrationTests @Autowired constructor(private val orderService: OrderService){
    // tests that use the embedded OrderService
}

Note that this function allows test dependencies to be final and therefore immutable.

If the spring.test.constructor.autowire.mode property is all ( see @TestConstructor), you can skip the declaration of the @Autowired annotation for the constructor from the previous example, resulting in the following.

Java

@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {
    private final OrderService orderService;
    OrderServiceIntegrationTests(OrderService orderService) {
        this.orderService = orderService;
    }
    // tests using the embedded OrderService
}
Kotlin

@SpringJUnitConfig(TestConfig::class)
class OrderServiceIntegrationTests(val orderService:OrderService) {
    // tests using the embedded OrderService
}
Dependency injection via method

If the parameter of a test method from JUnit Jupiter or a callback method is vital the test loop is of type ApplicationContext (or a subtype thereof) or annotated or meta-annotated with @Autowired, @Qualifier or @Value annotations, Spring injects the value for that particular parameter using the corresponding bean from the ApplicationContext test.

In the following example, Spring injects the OrderService from ApplicationContext loaded from TestConfig.class into the deleteOrder() test method:

Java

@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {
    @Test
    void deleteOrder(@Autowired OrderService orderService) {
        // use orderService from the ApplicationContext of the test
    }
}
Kotlin

@SpringJUnitConfig(TestConfig::class)
class OrderServiceIntegrationTests {
    @Test
    fun deleteOrder(@Autowired orderService: OrderService) {
        // use orderService from the ApplicationContext of the test
    }
}

Thanks to JUnit Jupiter's robust ParameterResolver support, multiple dependencies can be injected into one method, not only from Spring, but also from JUnit itself Jupiter or other third-party extensions.

The following example shows how to have Spring and JUnit Jupiter simultaneously inject dependencies into the placeOrderRepeatedly() test method.

Java

@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {
    @RepeatedTest(10)
    void placeOrderRepeatedly(RepetitionInfo repeatedInfo,
            @Autowired OrderService orderService) {
        // use orderService from the ApplicationContext of the test
        // and repetitionInfo from JUnit Jupiter
    }
}
Kotlin

@SpringJUnitConfig(TestConfig::class)
class OrderServiceIntegrationTests {
    @RepeatedTest (10)
    fun placeOrderRepeatedly(repetitionInfo:RepetitionInfo, @Autowired orderService:OrderService) {
        // use orderService from the ApplicationContext of the test
        // and repetitionInfo from JUnit Jupiter
    }
}

Note that using the @RepeatedTest annotation from JUnit Jupiter allows the test method to access RepetitionInfo.

Test class configuration with @Nested

Spring TestContext Framework supports the use of test-related annotations for test classes marked with the annotation @Nested, in JUnit Jupiter since Spring Framework 5.0; however, prior to Spring Framework 5.3, class-level test configuration annotations were not inherited from nested classes, as is the case with superclasses.

Spring Framework 5.3 introduced full-featured support for inheriting test class configuration from nested classes classes, and this configuration will be inherited by default. To change the default INHERIT mode to OVERRIDE mode, you can mark a single test class annotated with @Nested with the @NestedTestConfiguration(EnclosingConfiguration.OVERRIDE). An explicit @NestedTestConfiguration declaration will apply to the annotated test class as well as all its subclasses and nested classes. Thus, it is possible to annotate a high-level test class with the @NestedTestConfiguration annotation, and this will be applied to all its nested test classes recursively.

To allow development teams to change the default value to OVERRIDE - for example, to ensure compatibility with Spring Framework versions 5.0-5.2 - the default mode can be changed globally via a JVM system property or a spring.properties file in the root of the classpath. For details, see the note "Changing the default enclosing configuration inheritance mode".

Although the following "Hello World" example is very simplified, it shows how to declare configuration for a high-level class that is inherited by its test classes, marked with the @Nested annotation. In this particular example, only the configuration class TestConfig is inherited. Each nested test class exposes its own set of active profiles, resulting in a separate ApplicationContext for each nested test class (see "Caching context"). Refer to the list of supported annotations to see which annotations can be inherited in test classes with the @Nested.

Java

@SpringJUnitConfig(TestConfig.class)
class GreetingServiceTests {
    @Nested
    @ActiveProfiles("lang_en")
    class EnglishGreetings {
            @Test
            void hello(@Autowired GreetingService service) {
                assertThat(service.greetWorld()).isEqualTo("Hello World");
            }
    }
    @Nested
    @ActiveProfiles("lang_de")
    class GermanGreetings {
        @Test
        void hello(@Autowired GreetingService service) {
            assertThat(service.greetWorld()).isEqualTo("Hallo Welt");
        }
    }
}
Kotlin

@SpringJUnitConfig(TestConfig::class)
class GreetingServiceTests {
    @Nested
    @ActiveProfiles("lang_en")
    inner class EnglishGreetings {
        @Test
        fun hello(@Autowired service:GreetingService) {
            assertThat(service.greetWorld()).isEqualTo("Hello World")
        }
    }
    @Nested
    @ActiveProfiles("lang_de")
    inner class GermanGreetings {
        @Test
        fun hello(@Autowired service:GreetingService) {
            assertThat(service.greetWorld()).isEqualTo("Hallo Welt")
        }
    }
}        

TestNG Helper Classes

The org.springframework.test.context.testng package provides the following helper classes for TestNG-based testing cases:

  • AbstractTestNGSpringContextTests

  • AbstractTransactionalTestNGSpringContextTests

AbstractTestNGS SpringContextTestsis an abstract base test class that integrates the Spring TestContext Framework with explicit support for ApplicationContext testing into the TestNG framework. If you extend AbstractTestNGSpringContextTests, you can access a protected applicationContext instance variable that can be used to perform an explicit lookup of BINs or to test the state of the context as a whole.

AbstractTransactionalTestNGSpringContextTests is an abstract transactional AbstractTestNGSpringContextTests extension that adds some convenience features for accessing JDBC. This class expects the ApplicationContext to have a javax.sql.DataSource bean and a PlatformTransactionManager bean defined. If you extend AbstractTransactionalTestNGSpringContextTests, you can access the protected instance variable jdbcTemplate, which you can use to run SQL statements to query the database data. You can use such queries to validate the state of the database both before and after executing application code associated with the database, and Spring is guaranteed to ensure that such queries are executed within the same transaction as the application code. When used in conjunction with an ORM tool, false positives should be avoided. As mentioned in the JDBC Testing Support section, AbstractTransactionalTestNGSpringContextTests also provides helper methods that delegate authority to methods in JdbcTestUtils using the aforementioned jdbcTemplate. Additionally, AbstractTransactionalTestNGSpringContextTests provides the executeSqlScript(..) method for executing SQL scripts against the configured DataSource.

These classes are extension helpers. If you don't need your test classes to be tied to a Spring-specific class hierarchy, you can configure your own custom test classes using the @ContextConfiguration, @TestExecutionListeners and so on, and also manually instrument your test class using TestContextManager. See the source code of AbstractTestNGSpringContextTests for an example of how you can instrument your test class.