3

I am trying to test a Spring Controller using Mockito. I have mocked the object also used when() as per this question , but I am still facing the null pointer exception. Please suggest a way to solve this exception.

Github repository of this project

The particular line null pointer linked to is

 modelMap.put("categories", simpleCategoryDAO.getAllCategories());    

I have mocked simpleCategoryDAO and used when() to return a list for getAllCategories().

Test Method:

@RunWith(MockitoJUnitRunner.class)
public class CategoryControllerTest {   

    private MockMvc mockMvc;

    @InjectMocks
    private CategoryController categoryController;

    @Mock
    private SimpleCategoryDAO simpleCategoryDAO;

    @Before
    public void setup() {
        categoryController = new CategoryController();
        mockMvc = MockMvcBuilders.standaloneSetup(categoryController).build();
    }

    @Test
    public void categories_ShouldRenderCategoriesView() throws Exception {  
        List<Category> ALL_CATEGORIES = Arrays.asList(
                new Category(1,"Funny"),
                new Category(2,"JoyFul")
                );  
        Mockito.when(simpleCategoryDAO.getAllCategories()).thenReturn(ALL_CATEGORIES);  

        mockMvc.perform(get("/categories"))
        //.andExpect((MockMvcResultMatchers.model()).attribute("categories",ALL_CATEGORIES));
        .andExpect(MockMvcResultMatchers.view().name("categories"));
    }



}

Code of controller

@Controller
public class CategoryController {

    @Autowired
    SimpleCategoryDAO simpleCategoryDAO;

    @Autowired
    SimpleGifDAO simpleGifDAO;

    @RequestMapping("/categories")
    public String getAllCategories(ModelMap modelMap) {     
        modelMap.put("categories", simpleCategoryDAO.getAllCategories());       
        return "categories";        
    }

    @RequestMapping("/category/{categoryID}")
    public String getGifsByCategoryID(@PathVariable int categoryID,ModelMap modelMap){
        modelMap.put("gifs", simpleGifDAO.findGifsByCategoryID(categoryID));    
        modelMap.put("category",simpleCategoryDAO.getCategoryByID(categoryID));
        return "category";
    }
}

Exception:

java.lang.NullPointerException: null
    at com.teja.controller.CategoryController.getAllCategories(CategoryController.java:23) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_73]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_73]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_73]
    at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_73]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221) ~[spring-web-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137) ~[spring-web-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110) ~[spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:776) ~[spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:705) ~[spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959) ~[spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) ~[spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) [spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858) [spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) [tomcat-embed-core-8.0.23.jar:8.0.23]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) [spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:65) [spring-test-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) [tomcat-embed-core-8.0.23.jar:8.0.23]
    at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167) [spring-test-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134) [spring-test-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:144) [spring-test-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at CategoryControllerTest.categories_ShouldRenderCategoriesView(CategoryControllerTest.java:46) [classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_73]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_73]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_73]
    at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_73]
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) [junit-4.12.jar:4.12]
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.12.jar:4.12]
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) [junit-4.12.jar:4.12]
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) [junit-4.12.jar:4.12]
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) [junit-4.12.jar:4.12]
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) [junit-4.12.jar:4.12]
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]
    at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37) [mockito-core-1.10.19.jar:na]
    at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62) [mockito-core-1.10.19.jar:na]
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) [.cp/:na]
Community
  • 1
  • 1
mc20
  • 1,145
  • 1
  • 10
  • 26

2 Answers2

10

The problem is on your test class Before method you are instantiating new controller

@Before
    public void setup() {
        categoryController = new CategoryController();
        mockMvc = MockMvcBuilders.standaloneSetup(categoryController).build();
    }

Here is how I do test for Controller

Controller Class :

@Controller
public class CategoryController {

    private SimpleCategoryDAO simpleCategoryDAO;
    private SimpleGifDAO simpleGifDAO;

    @Autowired
    public void setSimpleGifDAO(SimpleGifDAO simpleGifDAO) {
        this.simpleGifDAO = simpleGifDAO;
    }

    @Autowired
    public void setSimpleCategoryDAO(SimpleCategoryDAO simpleCategoryDAO) {
        this.simpleCategoryDAO = simpleCategoryDAO;
    }

    @RequestMapping("/categories")
    public String getAllCategories(ModelMap modelMap) {
        modelMap.put("categories", simpleCategoryDAO.getAllCategories());
        return "categories";
    }

    @RequestMapping("/category/{categoryID}")
    public String getGifsByCategoryID(@PathVariable int categoryID, ModelMap modelMap) {
        modelMap.put("gifs", simpleGifDAO.findGifsByCategoryID(categoryID));
        modelMap.put("category", simpleCategoryDAO.getCategoryByID(categoryID));
        return "category";
    }
}

Notice I'm using setter injection here not field injection. You can also use constructor injection ( preferred way for me).

In you test class

@RunWith(MockitoJUnitRunner.class)
public class CategoryControllerTest {

    private MockMvc mockMvc;

    @Mock
    private SimpleCategoryDAO simpleCategoryDAO;

    @Before
    public void setup() {
        final CategoryController categoryController = new CategoryController();

        //notice here I'm setting the mocked dao here
        // if you didn't use @RunWith(MockitoJUnitRunner.class)
        // you can do: simpleCategoryDAO = Mockito.mock(SimpleCategoryDAO.class);

        categoryController.setSimpleCategoryDAO(simpleCategoryDAO);

        mockMvc = MockMvcBuilders.standaloneSetup(categoryController).build();
    }

    @Test
    public void categories_ShouldRenderCategoriesView() throws Exception {
        List<Category> ALL_CATEGORIES = Arrays.asList(
                new Category(1, "Funny"),
                new Category(2, "JoyFul")
        );
        Mockito.when(simpleCategoryDAO.getAllCategories()).thenReturn(ALL_CATEGORIES);

        mockMvc.perform(get("/categories"))
                //.andExpect((MockMvcResultMatchers.model()).attribute("categories",ALL_CATEGORIES));
                .andExpect(MockMvcResultMatchers.view().name("categories"));
    }
}

Take a look at Before method on test. I'm setting the mocked DAO on the new instance of controller that I've created and then I'm creating the MockMvc using same instance of controller.

A0__oN
  • 8,740
  • 6
  • 40
  • 61
  • Wouldn't it be simpler to test `CategoryController` by calling its methods directly, without using `mockMvc`? And the presence of annotations such as `@RequestMapping` could be verified through a custom assertion method. Just wondering... – Rogério Aug 05 '16 at 15:04
  • You can do the simple test for the controller without mockmvc. That method will test the controller method only as plain component rather than mockmvc here we are simulating the actual call to handler – A0__oN Aug 05 '16 at 16:55
  • Yes, but what exactly does "simulating the actual call to handler" gains us? – Rogério Aug 05 '16 at 16:58
  • we can check if the HttpStatus code are returned correctly. We can check if the method specified as GET can be accessed throught POST or vice versa. We can check what kind of content is returned. If we set the returned content to JSON we can check in our test if JSON was actually returned. Again we can use jsonPath library to check if json was created exactly as we wanted to send – A0__oN Aug 05 '16 at 17:01
  • take a look at this article. https://www.petrikainulainen.net/programming/spring-framework/unit-testing-of-spring-mvc-controllers-rest-api/ – A0__oN Aug 05 '16 at 17:03
1

I had some issues with the provided solution so I thought I'd share how I solved it. My issue was that when the controller called a method on the DAO it was throwing NullPointerException, because I didn't want to use setter injection to provide the mocked DAO to the controller (I refused to create a setter method in the DAO just to be able to test everything).

Basically for the OP it would require:

  1. NOT instantiating the controller in the setup() method (like Aman pointed out)
  2. Declaring the DAO with the @Mock annotation before the controller declaration.

Like so:

@RunWith(MockitoJUnitRunner.class)
public class CategoryControllerTest {   

    private MockMvc mockMvc;

    @Mock
    private SimpleCategoryDAO simpleCategoryDAO;

    @InjectMocks
    private CategoryController categoryController;

    @Before
    public void setup() {
        mockMvc = MockMvcBuilders.standaloneSetup(categoryController).build();
    }

    @Test
    public void categories_ShouldRenderCategoriesView() throws Exception {  
        List<Category> ALL_CATEGORIES = Arrays.asList(
                new Category(1,"Funny"),
                new Category(2,"JoyFul")
                );  
        Mockito.when(simpleCategoryDAO.getAllCategories()).thenReturn(ALL_CATEGORIES);  

        mockMvc.perform(get("/categories"))
        //.andExpect((MockMvcResultMatchers.model()).attribute("categories",ALL_CATEGORIES));
        .andExpect(MockMvcResultMatchers.view().name("categories"));
    }



}
flowjoe
  • 105
  • 8