簡體   English   中英

使用Mockito的Spring控制器測試,空指針異常

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

我正在嘗試使用Mockito測試Spring Controller。 我已經按照這個問題嘲笑了還使用when()的對象,但是我仍然面臨着空指針異常。 請提出解決此異常的方法。

該項目的Github存儲庫

鏈接到的特定行空指針是

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

我模擬了simpleCategoryDAO並使用when()返回getAllCategories()的列表。

測試方法:

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



}

控制器代號

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

例外:

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]

問題出在test class Before method you are instantiating new controller

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

這是我如何測試Controller

控制器類別:

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

注意,我在這里使用的是setter注入,而不是現場注入。 您還可以使用構造函數注入(對我來說是首選方法)。

在你的考試課上

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

看一下測試Before method 我在創建的 控制器 實例上設置了mocked DAO ,然后使用相同的控制器實例創建MockMvc

提供的解決方案存在一些問題,因此我想與您分享解決方案。 我的問題是,當控制器在DAO上調用方法時,它拋出NullPointerException,因為我不想使用setter注入將模擬的DAO提供給控制器(我拒絕在DAO中創建setter方法只是為了能夠測試一切)。

基本上,對於OP它將需要:

  1. 不要在setup()方法中實例化控制器(就像Aman指出的那樣)
  2. 在控制器聲明之前使用@Mock注釋聲明DAO。

像這樣:

@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