zoukankan      html  css  js  c++  java
  • SpringMVC Controller层的单元测试

    Getting Ready

    测试相关Maven dependency如下:

    <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.0.3.RELEASE</version>
            <scope>test</scope>
        </dependency>
        
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>1.9.5</version>
            <scope>test</scope>
        </dependency>
        
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
          <dependency> 
              <groupId>org.hamcrest</groupId> 
              <artifactId>hamcrest-core</artifactId> 
              <version>1.3</version> 
              <scope>test</scope> 
          </dependency>

    关于Spring项目的一些依赖如(spring-context, spring-web, spring-webmvc, spring-beans),这里就不列举了

    Example

    Reference Class

    Controller 如下:

    @Controller
    @RequestMapping("/")
    public class DemoController {
    
        @Autowired
        private TestProjectService testProjectService;
    
        @RequestMapping(value = "jsonCompare", method = RequestMethod.POST)
        @ResponseBody
        public List<FieldComparisonFailure> jsonCompare(@RequestParam("expect") String expect, @RequestParam("actual") String actual, ModelMap model,
                HttpSession session) {
    
            List<FieldComparisonFailure> list = testProjectService.compare(expect, actual);
    
            return list;
        }
    
    }

    FieldComparisonFailure类如下

    /**
     * Models a failure when comparing two fields.
     */
    public class FieldComparisonFailure {
        private final String field;
        private final Object expected;
        private final Object actual;
    
        public FieldComparisonFailure(String field, Object expected, Object actual) {
            this.field = field;
            this.expected = expected;
            this.actual = actual;
        }
    
        public String getField() {
            return field;
        }
    
        public Object getExpected() {
            return expected;
        }
    
        public Object getActual() {
            return actual;
        }
    }

    TestProjectService接口如下:

    public interface TestProjectService {
        public List<FieldComparisonFailure> compare(String expect, String actual);
    
    }

    TestProjectServiceImpl 具体实现是比较两个Json字符串 返回一个List

    @Service
    public class TestProjectServiceImpl implements TestProjectService {
        
        @Override
        public List<FieldComparisonFailure> compare(String expect, String actual) {
            
            Comparator comparator = new Comparator();
            List<FieldComparisonFailure> list = new ArrayList<FieldComparisonFailure>();
            
            try {
                list = comparator.compare(expect, actual);
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return list;
        }
    
    }

     ##转载注明出处:http://www.cnblogs.com/wade-xu/p/4311657.html 

    Unit Test

    首先来看一个独立的单元测试方式, 这个例子用Mockito 模拟service层以便隔离controller的测试。

    package com.wadeshop.controller;
    
    import static org.mockito.Mockito.when;
    import static org.mockito.Mockito.times;
    import static org.mockito.Mockito.verify;
    import static org.mockito.Mockito.verifyNoMoreInteractions;
    
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
    import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
    import static org.hamcrest.Matchers.hasSize;
    
    import org.junit.Before;
    import org.junit.Test;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.MockitoAnnotations;
    import org.springframework.http.MediaType;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    
    import com.google.common.collect.ImmutableList;
    import com.wadeshop.service.TestProjectService;
    import com.wadeshop.entity.FieldComparisonFailure;
    
    public class DemoControllerTest_mock {
        
        @Mock
        private TestProjectService testProjectService;
        
        @InjectMocks
        private DemoController demoController;
     
        private MockMvc mockMvc;
     
        @Before
        public void setup() {
     
            // initialize mock object
            MockitoAnnotations.initMocks(this);
            
            // Setup Spring test in standalone mode
            this.mockMvc = MockMvcBuilders.standaloneSetup(demoController).build();
        }
        
        @Test
        public void test() throws Exception {
            
            //prepare test data
            FieldComparisonFailure e1 = new FieldComparisonFailure("Number", "3", "4");
            FieldComparisonFailure e2 = new FieldComparisonFailure("Number", "1", "2");
            
            //actually parameter haven't use, service was mocked
            String expect = "";
            String actual = "";
            
            //Sets a return value to be returned when the method is called
            when(testProjectService.compare(expect, actual)).thenReturn(ImmutableList.of(e1, e2));
            
            //construct http request and expect response
           this.mockMvc
                .perform(post("/jsonCompare")
                         .accept(MediaType.APPLICATION_JSON)
                   .param("actual", actual)
                   .param("expect", expect))
                   .andDo(print()) //print request and response to Console
                   .andExpect(status().isOk())
                   .andExpect(content().contentType("application/json;charset=UTF-8"))
                   .andExpect(jsonPath("$", hasSize(2)))
                   .andExpect(jsonPath("$[0].field").value("Number"))
                   .andExpect(jsonPath("$[0].expected").value("3"))
                   .andExpect(jsonPath("$[0].actual").value("4"))
                   .andExpect(jsonPath("$[1].field").value("Number"))
                   .andExpect(jsonPath("$[1].expected").value("1"))
                   .andExpect(jsonPath("$[1].actual").value("2"));
           
             //verify Interactions with any mock
             verify(testProjectService, times(1)).compare(expect, actual);
             verifyNoMoreInteractions(testProjectService);
        }
    }

    @Mock: 需要被Mock的对象

    @InjectMocks: 需要将Mock对象注入的对象, 此处就是Controller

    Before test

    初始化Mock对象, 通过MockMvcBuilders.standaloneSetup模拟一个Mvc测试环境,注入controller, 通过build得到一个MockMvc, 后面就用MockMvc的一些API做测试。

    这不是真实的Spring MVC环境,如果要模拟真实环境需要用 MockMvcBuilders.webAppContextSetup(webApplicationContext).build(), 见下文。

    测试方法里面需要构建测试数据,mock service调用方法,返回一个ImmutableList (google-collections 谷歌的集合库)

    然后构造http请求并且传入参数执行, 最后断言验证期望结果, 关于JsonPath的使用请参考http://goessner.net/articles/JsonPath/

    运行

    andDo(print()) 打印到控制台的信息如下

    复制代码
    MockHttpServletRequest:
             HTTP Method = POST
             Request URI = /jsonCompare
              Parameters = {actual=[], expect=[]}
                 Headers = {Accept=[application/json]}
    
                 Handler:
                    Type = com.wadeshop.controller.DemoController
    
                   Async:
       Was async started = false
            Async result = null
    
      Resolved Exception:
                    Type = null
    
            ModelAndView:
               View name = null
                    View = null
                   Model = null
    
                FlashMap:
    
    MockHttpServletResponse:
                  Status = 200
           Error message = null
                 Headers = {Content-Type=[application/json;charset=UTF-8]}
            Content type = application/json;charset=UTF-8
                    Body = [{"field":"Number","actual":"4","expected":"3"},{"field":"Number","actual":"2","expected":"1"}]
           Forwarded URL = null
          Redirected URL = null
                 Cookies = []
    复制代码

     ##转载注明出处:http://www.cnblogs.com/wade-xu/p/4311657.html 

    Integration Testing

    再来看集成Web环境方式, 这次仍然使用Spring MVC Test 但还需要加载 WebApplicationContext

    import static org.hamcrest.Matchers.hasSize;
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
    import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
    
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.MediaType;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.springframework.test.context.web.WebAppConfiguration;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    import org.springframework.web.context.WebApplicationContext;
    
    @RunWith(SpringJUnit4ClassRunner.class)  
    @WebAppConfiguration(value = "src/main/webapp")  
    @ContextConfiguration("file:src/main/resources/applicationContext.xml")
    
    public class DemoControllerTest {
        
        @Autowired
        private WebApplicationContext wac;
        private MockMvc mockMvc;
    
        @Before
        public void setUp() {
            mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
        }
    
        @Test
        public void test() throws Exception {
            String actual = "{"orderNumber": "4955","orderVersion": "1"}";
            String expect = "{"orderNumber": "4956","orderVersion": "1"}";
            
           this.mockMvc
                .perform(post("/jsonCompare")
                         .accept(MediaType.APPLICATION_JSON)
                   .param("actual", actual)
                   .param("expect", expect))
                   .andDo(print())
                   .andExpect(status().isOk())
                   .andExpect(content().contentType("application/json"))
                   .andExpect(jsonPath("$", hasSize(1)))
                   .andExpect(jsonPath("$[0].field").value("orderNumber"))
                   .andExpect(jsonPath("$[0].actual").value("4955"))
                   .andExpect(jsonPath("$[0].expected").value("4956"));
        }
    }

    @RunWith: 告诉Junit使用 Spring-Test 框架, 允许加载web 应用程序上下文。

    @WebAppConfiguration: 表明该类会使用web应用程序的默认根目录来载入ApplicationContext, value = "src/main/webapp" 可以不填,默认此目录

    @ContextConfiguration: 指定需要加载的spring配置文件的地址 ("file:src/main/resources/applicationContext.xml") 

    @Autowired WebApplicationContext wac:注入web环境的ApplicationContext容器;

    使用MockMvcBuilders.webAppContextSetup(wac).build()来创建一个MockMvc进行测试, 此为模拟真实的Spring MVC环境

    测试过程和前面一个例子大体相似,唯一的区别就是,这次传入的是真实的参数,调用真实的service取得返回值。

    运行时间比较长

    控制台信息

    复制代码
    MockHttpServletRequest:
             HTTP Method = POST
             Request URI = /jsonCompare
              Parameters = {actual=[{"orderNumber": "4955","orderVersion": "1"}], expect=[{"orderNumber": "4956","orderVersion": "1"}]}
                 Headers = {Accept=[application/json]}
    
                 Handler:
                    Type = com.wadeshop.controller.DemoController
    
                   Async:
       Was async started = false
            Async result = null
    
      Resolved Exception:
                    Type = null
    
            ModelAndView:
               View name = null
                    View = null
                   Model = null
    
                FlashMap:
    
    MockHttpServletResponse:
                  Status = 200
           Error message = null
                 Headers = {Content-Type=[application/json]}
            Content type = application/json
                    Body = [{"field":"orderNumber","actual":"4955","expected":"4956"}]
           Forwarded URL = null
          Redirected URL = null
                 Cookies = []
    复制代码

    从上面的例子来看集成测试Spring MVC controller是不是也很简单, 稍微配置一下就行了。

     ##转载注明出处:http://www.cnblogs.com/wade-xu/p/4311657.html 

    总结

    单元测试过程无非就这三部曲:

    1. 准备 (测试环境,测试数据)
    2. 执行 (构造请求传入参数执行)
    3. 断言 (验证结果)

    Troubleshooting

    如果发现一些NoClassDefFoundError, 估计依赖的jar版本太旧了。

    import 哪些类不要弄错了,有些需要static import, IDE工具不一定会提示导入

  • 相关阅读:
    二维数组问题
    如何在Windows环境下寻找并杀死进程
    关于使用YYYY-MM-dd产生BUG的问题
    boolean在Java中占几个字节的问题
    IDEA启动项目时报错:Error running 'Application': Command line is too long. Shorten command line for Application or also for Spring Boot default configuration.
    mvn package失败,不再支持源选项 1.5。请使用 1.6 或更高版本。
    17蓝桥杯竞赛题“购物单”
    17蓝桥杯竞赛题“取数位”
    微信公众平台开发(ASP.NET)
    宋艳杰个人作品集合
  • 原文地址:https://www.cnblogs.com/zhizhao/p/7814208.html
Copyright © 2011-2022 走看看