hublo
hublo

Reputation: 1180

Spring Boot Unit Test Autowired

I have the following classes :

ApplicationAndConfiguration class

package mypackage.service;

import mypackage.service.util.MyUtility;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class ApplicationAndConfiguration {


    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(ApplicationAndConfiguration.class, new String[]{});
    }

    @Bean(initMethod="init")
    public MyUtility birtUtil() {
        return new MyUtility();
    }
}

MyRestController class

package mypackage.service.controllers;

import mypackage.service.util.MyUtility;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyRestController {

    @Autowired
    private MyUtility util;

    @RequestMapping("/getLibraryName")
    public String getMessageFromRest(@RequestParam String name) {

        return "name was " + name + "//" + util.getMessage();
    }   
}

MyUtility class

package mypackage.service.util;

public class MyUtility {

    private String message;

    public void init() {
        setMessage("MyUtility correctly initialized!");
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

When I start the application and run it as an independant jar, or from the IDE (Eclipse), no problem at all, everything works as expected.

However, I want to write a unit test to test my MyRestController class ... and I'm getting a NPE because the Autowired field util is null (within MyRestController class).

Here is my test class :

package mypackage.service.controllers;

import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import mypackage.service.ApplicationAndConfiguration;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

@SpringApplicationConfiguration(classes = ApplicationAndConfiguration.class)
@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class TestController {

    private MockMvc mvc;

    @Before
    public void setup() throws Exception {
        mvc = MockMvcBuilders.standaloneSetup(new MyRestController()).build();
    }

    @Test
    public void MyTestController() throws Exception {

        mvc.perform(MockMvcRequestBuilders.get("/getLibraryName").param("name", "test").accept(MediaType.APPLICATION_JSON))
        .andExpect(status().isOk())
        .andExpect(content().string(equalTo("name was test//MyUtility correctly initialized!")));
    }
}

I'm definitely missing something so that my Autowired field gets filled during tests, and not only during standard application execution ...

Any pointer why it doesn't work ?

Upvotes: 40

Views: 103172

Answers (2)

Marcello DeSales
Marcello DeSales

Reputation: 22309

Since SpringBoot 1.4, all the classes changed and deprecated https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-1.4.0-M2-Release-Notes. Replace the Runner and Configuration with the ones below. SpringRunner will detect the test framework for you.

@RunWith(SpringRunner.class)
@SpringBootTest(classes = { FileService.class, AppProperties.class, DownloadConfigEventHandler.class })
@EnableConfigurationProperties
public class ConfigMatrixDownloadAndProcessingIntegrationTests extends ConfigMatrixDownloadAbstractTest {

  // @Service FileService
  @Autowired
  private FileService fileService;

  // @Configuration AppProperties
  @Autowired
  private AppProperties properties;

  // @Compoenet DownloadConfigEventHandler
  @Autowired
  private DownloadConfigEventHandler downloadConfigEventHandler;    
  ..
  ..
}

All of these instances will be autowired as expected! Even Spring Events with the Publisher is working as expected as in https://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2.

Upvotes: 44

luboskrnac
luboskrnac

Reputation: 24561

MockMvc standalone setup is for unit testing. You are doing integration testing when you are creating Spring context in test. Don't mix these two types of testing.

So just change it this way:

@SpringApplicationConfiguration(classes = ApplicationAndConfiguration.class)
@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class TestController {

    private MockMvc mvc;

    @Autowired
    private WebApplicationContext webApplicationContext;

    @Before
    public void setup() throws Exception {
        mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }

Upvotes: 14

Related Questions