Search code examples
javaspringspring-mvcspring-mvc-testspring-ioc

Spring MVC Test failing


I am trying to run a Spring MVC Test but keep getting this exception.

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException

The exception is occuring because the autowired dependency,

    @Autowired
    private AccountService accountService;

is not getting injected in the test (works fine outside of the test).

Can anyone help me with this. Here is my code:

//AccountControllerITest Class

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MockServletContext.class)
@WebAppConfiguration
public class AccountControllerITest {

private MockMvc mvc;

ObjectMapper om;

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

@Test
public void getAccounts() throws Exception {
    MvcResult mvcResult =     mvc.perform(MockMvcRequestBuilders.get("/api/accounts"))
            .andExpect(status().isOk())
            .andReturn();
}    
}

}

//AccountController

@RestController
@RequestMapping("/api/accounts")
public class AccountController {

@Autowired
private AccountService accountService;

@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Set<AccountInfo>> getAccounts(@RequestParam(value = "firstName", required = false) String firstName,
                                                    @RequestParam(value = "surName", required = false)  String surName) {
    Set<AccountInfo> accounts = accountService.getAccounts(firstName, surName);
    return new ResponseEntity<>(accounts, HttpStatus.OK);
}

}

Thanks for the help!


Solution

  • Because you are using standalone setup: mvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();. If you create controller via new AccountController(), Spring doesn't have a chance to inject accountService as it doesn't control instances creation and wiring.

    You have two options:

    1. Switch your test to unit test and do not use SpringJUnit4ClassRunner nor MockServletContext at all. You can use @InjectMocks to inject private accountService:

      public class AccountControllerITest {
      
          private MockMvc mvc;
      
          ObjectMapper om;
      
          @Mock
          private AccountController accountController = new AccountController();
      
          @InjectMocks
          private AccountService accountService = new Mockito.mock(AccountService.class);
      
          @Before
          public void setUp() throws Exception {
              mvc = MockMvcBuilders.standaloneSetup(accountController).build();
          }
      

      There is also enhancement you can apply to your controller. Replace field injection with constructor injection and you can pass accountService to controller via constructor in test. This way you don't need to use @InjectMocks at all.

    2. Use webAppContext setup:

      @Autowired
      private WebApplicationContext webApplicationContext;
      
      @BeforeMethod
      public void init() {
          mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
      }