简体   繁体   English

使用Mockito的Spring控制器测试,空指针异常

[英]Spring Controller Testing using Mockito , Null Pointer Exception

I am trying to test a Spring Controller using Mockito. 我正在尝试使用Mockito测试Spring Controller。 I have mocked the object also used when() as per this question , but I am still facing the null pointer exception. 我已经按照这个问题嘲笑了还使用when()的对象,但是我仍然面临着空指针异常。 Please suggest a way to solve this exception. 请提出解决此异常的方法。

Github repository of this project 该项目的Github存储库

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() . 我模拟了simpleCategoryDAO并使用when()返回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]

The problem is on your test class Before method you are instantiating new controller 问题出在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

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. 注意,我在这里使用的是setter注入,而不是现场注入。 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. 看一下测试Before method 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. 我在创建的 控制器 实例上设置了mocked DAO ,然后使用相同的控制器实例创建MockMvc

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). 我的问题是,当控制器在DAO上调用方法时,它抛出NullPointerException,因为我不想使用setter注入将模拟的DAO提供给控制器(我拒绝在DAO中创建setter方法只是为了能够测试一切)。

Basically for the OP it would require: 基本上,对于OP它将需要:

  1. NOT instantiating the controller in the setup() method (like Aman pointed out) 不要在setup()方法中实例化控制器(就像Aman指出的那样)
  2. Declaring the DAO with the @Mock annotation before the controller declaration. 在控制器声明之前使用@Mock注释声明DAO。

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"));
    }



}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM