junit: NullPointer on injected bean when testing controller with mockmvc

11,974

Solution 1

From Spring documentation:

The "webAppContextSetup" loads the actual Spring MVC configuration resulting in a more complete integration test. Since the TestContext framework caches the loaded Spring configuration, it helps to keep tests running fast even as more tests get added. Furthermore, you can inject mock services into controllers through Spring configuration, in order to remain focused on testing the web layer.

The "standaloneSetup" on the other hand is a little closer to a unit test. It tests one controller at a time, the controller can be injected with mock dependencies manually, and it doesn’t involve loading Spring configuration. Such tests are more focused in style and make it easier to see which controller is being tested, whether any specific Spring MVC configuration is required to work, and so on. The "standaloneSetup" is also a very convenient way to write ad-hoc tests to verify some behavior or to debug an issue.

From this I understand that, if I need an .xml as a Spring configuration file to be loaded, then I need to go with the "webAppContextSetup". If I just need very simple tests with my Controller, without the need of a Spring configuration, then I go with the "standaloneSetup" approach. In your case, I think you need the "webAppContextSetup".

Solution 2

You forgot to create your mock objects and then inject them into your controller like so

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:rest-context-test.xml")
public class CustomerControllerTest 
{
    @Mock
    private CustomerService customerService;
    @InjectMocks
    private CustomerController customerController;

    private MockMvc mockMvc;

    @Before
    public void setup() 
    {
        MockitoAnnotations.initMocks(this);
        this.mockMvc = MockMvcBuilders
                .standaloneSetup(customerController).build();
    }


    @Test
    public void testGetCustomerById() throws Exception 
    {
        mockMvc.perform(get("/customers/{ccid}", 1)).andExpect(status().isOk())
                .andExpect(content().contentType("application/json"))
                .andExpect(jsonPath("$.ccid").value("1"));
    }
}
Share:
11,974
Lurk21
Author by

Lurk21

Updated on June 04, 2022

Comments

  • Lurk21
    Lurk21 almost 2 years

    So I have this test case:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:rest-context-test.xml")
    public class CustomerControllerTest {
    
    private MockMvc mockMvc;
    
    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders
                .standaloneSetup(new CustomerController()).build();
    }
    
    
    @Test
    public void testGetCustomerById() throws Exception {
        mockMvc.perform(get("/customers/{ccid}", 1)).andExpect(status().isOk())
                .andExpect(content().contentType("application/json"))
                .andExpect(jsonPath("$.ccid").value("1"));
    }
    
    }
    

    That loads rest-context-test.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd">
    
    <bean id="customerServiceImpl"
        class="myorg.service.impl.MockCustomerServiceImpl" />
    
    </beans>
    

    With the hope of being able to AutoWire customerServiceImpl into my controller:

    @Controller
    @RequestMapping("customers")
    public class CustomerController {
    
    private static Logger Logger = LoggerFactory
            .getLogger(CustomerController.class);
    
    @Autowired
    @Qualifier("customerServiceImpl")
    private CustomerService customerService;
    
    
    @RequestMapping(value = "/{ccid}", method = RequestMethod.GET, produces = "application/json")
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Customer getCustomerById(@PathVariable Integer ccid)
            throws BadRequestException, ServerErrorException,
            ResourceNotFoundException {
        Customer c = null;
    
        try {
            c = customerService.getCustomer(ccid);
        } catch (IllegalArgumentException iae) {
            // HTTP 400
            Logger.error("Invalid arguments submitted", iae);
            throw new BadRequestException();
        } catch (CsspException ce) {
            // HTTP 500
            Logger.error("Server error", ce);
            throw new ServerErrorException();
        }
    
        if (c == null) {
            // HTTP 404
            throw new ResourceNotFoundException();
        }
    
        return c;
    }
    }
    

    But my unit test is throwing the following trace:

    org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:965)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:844)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:689)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:829)
    at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:66)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
    at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:168)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:136)
    at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:141)
    at myorg.rest.controller.CustomerControllerTest.testGetCustomerById(CustomerControllerTest.java:41)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:46)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
    Caused by: java.lang.NullPointerException
    at myorg.rest.controller.CustomerController.getCustomerById(CustomerController.java:134)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle    (ServletInvocableHandlerMethod.java:104)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod    (RequestMappingHandlerAdapter.java:745)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal    (RequestMappingHandlerAdapter.java:686)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:953)
    ... 38 more
    

    This null pointer is thrown when the Controller tries to access customerService:

    @Autowired
    @Qualifier("customerServiceImpl")
    private CustomerService customerService;
    

    Why isn't my MockCustomerServiceImpl being AutoWired into the Controller?

    Note: package names have been changed to protect the innocent

  • Lurk21
    Lurk21 almost 10 years
    This was it. Thanks.
  • Pratz
    Pratz about 9 years
    webAppContextSetup is for integration tests but if you want to unit test your controller then you should always inject mocks as mentioned by ndrone
  • Nikolas
    Nikolas almost 9 years
    I think, method setUp() is missing customerController = new CustomerController();