What does this do: @RunWith(SpringJUnit4ClassRunner.class)

SpringJunit

Spring Problem Overview


What does this annotation do?
When would I want to use it?
When would I not want to use it?

@RunWith(SpringJUnit4ClassRunner.class)

I can find more usages of this when I Google and do not find a 101 explanation as to what this annotation is supposed to communicate to me or when/why I would use it?

Spring Solutions


Solution 1 - Spring

The annotation is used to configure a unit test that required Spring's dependency injection.

From Spring Reference - 10. Unit Testing:

> 10.1 Creating a Unit Test Class > > In order for the unit test to run a batch job, the framework must load the job's ApplicationContext. Two annotations are used to trigger this: > > @RunWith(SpringJUnit4ClassRunner.class): Indicates that the class should use Spring's JUnit facilities. > > @ContextConfiguration(locations = {...}): Indicates which XML files contain the ApplicationContext.

Solution 2 - Spring

If you are using annotations rather than XML files, then any class that you are unit testing that requires Spring dependency injection needs to be put into the @ContextConfiguration annotation. For example:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = FooManager.class)
class FooManagerTest {
    
    @Autowired
    FooManager fooManager;

Now when you use fooManager in a unit test it will have have a Spring context setup for it.

If fooManager autowires in any beans then those bean's classes also need to be in the @ContextConfiguration annotation. So if fooManager autowires in a FooReporter bean:

@ContextConfiguration(classes = {FooManager.class, FooReporter.class})

If the beans that fooManager autowires in contain state, then you will likely want to reset the state of those beans for each test. In that case you can add the @DirtiesContext annotation to your test class:

@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)

If fooManager or any of its autowired beans reads Spring config then you need to add an initializers list to the @ContextConfiguration annotation, that contains the ConfigFileApplicationContextInitializer class:

@ContextConfiguration(classes = {FooManager.class, FooReporter.class}, initializers = ConfigFileApplicationContextInitializer.class)

Solution 3 - Spring

To answer the when you would and wouldn't want to use it part of the question.

When to use SpringJUnit4ClassRunner

IMO SpringJUnit4ClassRunner should be used very sparingly. There is a significant overhead involved with starting up a Spring container to run a unit test.

I typically use SpringJUnit4ClassRunner to test:

  • that components are injected (auto-wired) as expected
  • that configuration data is injected as expected

When you are injecting components issues can arise if the @Qualifier annotation is not used or used incorrectly, for example.

When loading configuration from multiple yaml files you may want to test that maps are being merged as expected, with the appropriate overrides occurring.

At the very least I always have a simple SpringJUnit4ClassRunner test as a sanity check that the Spring container starts up OK.

When not to use SpringJUnit4ClassRunner

I would not use SpringJUnit4ClassRunner to test the non-Spring related functionality in my code under test. Which in my experience means most of the functionality.

So this means that any autowired components and injected config data needs to be mocked. This can mean quite a bit of setup code for your unit tests. However this setup code only needs to be written once for all the tests in your class under test. It is also much quicker to run unit tests with mocked components.

I keep the mocking simple and use Spock to mock the components. Example groovy code:

import spock.lang.Specification

class FooManagerTest extends Specification {

  FooManager cut

  void createMockFooReporter() {
    FooReporter mockFooReporter = Mock(FooReporter)
    mockFooReporter.fooFormatter = Mock(FooFormatter)
  }

  void setup() {
    cut = new FooManager()
    cut.fooReporter = createMockFooReporter()
  }

  void "Basic test"() {
     // Do a basic test using 'cut'
  }

}

In this example the class under test FooManager has an autowired FooReporter which itself contains an autowired FooFormatter.

Solution 4 - Spring

I think @RunWith annotation is in order to initialize the context of spring. Because the junit5 is released, you just can replace it with @SpringJUnitConfig.By the way, @RunWith annotation is already replaced by @ExtendWith, but you still can use it.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJay VeeView Question on Stackoverflow
Solution 1 - SpringRicardo VeguillaView Answer on Stackoverflow
Solution 2 - SpringJoman68View Answer on Stackoverflow
Solution 3 - SpringJoman68View Answer on Stackoverflow
Solution 4 - Springdragoncat12138View Answer on Stackoverflow