junit: NullPointer on injected bean when testing controller with mockmvc
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"));
}
}
Lurk21
Updated on June 04, 2022Comments
-
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 almost 10 yearsThis was it. Thanks.
-
Pratz about 9 yearswebAppContextSetup is for integration tests but if you want to unit test your controller then you should always inject mocks as mentioned by ndrone
-
Nikolas almost 9 yearsI think, method
setUp()
is missingcustomerController = new CustomerController();