9

I have a very simple rest controller:

@RestController
public class MyController {
    @Autowired
    public Logger logger;

The logger dependency gets injected via the following configuration:

@Configuration
public class MyConfig {
    @Bean
    public Logger logger() {
        return LoggerFactory.getLogger(MyController.class);
    }

If I run the Spring application that contains the controller then everything works fine. However, I cannot manage to achieve this dependency injection when running my unit tests. In this case I have the following test configuration:

@Configuration
@Profile("test")
public class MyTestConfig {
    @Bean
    public Logger logger() {
        return LoggerFactory.getLogger(MyCOntroller.class);
    }

And this is the relevant part of my unit tests code:

@RunWith(MockitoJUnitRunner.class)
@ContextConfiguration(classes = MyTestConfig.class)
@ActiveProfiles("test")
public class MyContollerTest {

However the logger object does not get "autowired" in MyController (note that I do not want to mock the logger object), which results in a null pointer reference.

What am I missing?

2

2 Answers 2

12

A unit test shouldn't use any Spring configuration. You should simply instantiate your component, and inject dependencies (usually fake ones) manually.

You used field injection, which makes it a bit harder. With constructor injection, all you would need to do is

Logger logger = LoggerFactory.getLogger(MyController.class);
MyController controller = new MyController(logger);

Mockito can help injecting fake dependencies for you, though, even when using field injection, thanks to the @Mock, @Spy and @InjectMocks annotations:

@Spy
private Logger logger = LoggerFactory.getLogger(MyController.class);

@InjectMocks
private MyController controller;

@Before
public void prepare() {
    MockitoAnnotations.initMocks(this);
}

That said, if I'm not mistaken, you're not using @RunWith(SpringJUnit4ClassRunner.class), so your test runner doesn't know anything about Spring, and thus doesn't create or use any Spring configuration.

Sign up to request clarification or add additional context in comments.

2 Comments

Yeah, the SpringJUnit4ClassRunner is the real culprit here, imo. This is more of an integration than a unit test.
It seems I took the wrong approach when writing unit tests with Spring then. Using field injection and leaving Spring out in the unit tests seems to be the way to go here. Thanks for the explanation.
1

I agree that a unit test shouldn't ideally include spring configuration, however; if you still wish to perform a unit test with Spring managed bean(s) you can use SpringExtension's parameter resolver.

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/context/junit/jupiter/SpringExtension.html

https://rieckpil.de/what-the-heck-is-the-springextension-used-for/

    @RunWith(MockitoJUnitRunner.class)
    @ContextConfiguration(classes = MyTestConfig.class)
    @ActiveProfiles("test")
    // New addition
    @ExtendWith(SpringExtension.class)
        @Import(
            value = {
        MyController.class,Myconfig.class, MyTestConfig.class
    }

    public class MyContollerTest {

Note, SpringExtension is used automatically when the @SpringBootTest annotation is used, however; for Controller tests like in this example, it has to be added explicitly.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.