zoukankan      html  css  js  c++  java
  • 其他

    测试

    有两个很重要的测试时单元测试和集成测试,通常是从单元测试开始测试类中的单个方法,然后进行集成测试,以测试不同的模块是否可以无缝协同工作。

    单元测试

    我们应该对控制器中的每个方法进行单元测试。但是测试Controller有一些要求,因为它们通常与Servlet API对象(如HttpServletRequest,HttpServletResponse,HttpSession等)交互。我们需要模拟这些对象以正确测试控制器。Spring-Test模拟对象是专门为使用Spring而构建的,并且与真实环境更接近,更容易使用。当然,我们需要先导入spring-test.jar这个jar。

    当调用控制器时,你可能需要传递HttpServletRequest和HttpServletResponse。在生产环境中,这两个的对象都有Servlet容器本身提供。在测试环境中,你可以使用org.springframework.mock.web包下的Spring MockHttpServletRequest和MockHttpServletResponse类:

    MockHttpServletRequest request=new MockHttpServletRequest();
    MockHttpServletResponse response=new MockHttpServletResponse();

    MockHttpServletRequet类实现了javax.servlet.http.HttpServletRequest,并允许你将实例配置的看起来像一个真正的HttpServletRequest。它提供了方法来设置HttpServletRequest中的所有属性以及获取其属性值:

    方法 描述
    setRequestURI 设置请求URI
    setParameter 设置一个参数值
    setMethod 设置HTTP方法
    set/getAttribute 设置/获得一个属性
    addHeader 添加一个请求头
    addParameter 添加一个请求参数
    getCookies 获得所有的Cookies
    getContextPath 返回上下文

    MockHttpServletResponse实现了javax.servlet.http.HttpServletResponse,并提供了配置方法:

    方法 描述
    addCookie 添加一个Cookie
    addHeader 添加一个·HTTP响应头
    getContentLength 返回内容长度
    getWriter 返回Writer
    getOutputStream 返回ServletOutputStream

    例子:

    我们先来写一个简单的Controller,它拿到一个名为id的请求参数,之后进行一些处理:

        /**
         * 单元测试SpringMVC
         */
        @RequestMapping("/testMock")
        public String testMock(HttpServletRequest request,HttpServletResponse response) {
            Integer id=(Integer) request.getAttribute("id");
            if(id==null) {
                response.setStatus(500);
            }else if(id==1) {
                request.setAttribute("viewed", 100);
            }else if(id==2) {
                request.setAttribute("viewed", 200);
            }
            return "mockView";
        }

    若请求属性id存在且值为1或2,则添加请求属性“viewed”,否则,不添加请求属性

    我们写两个测试方法进行测试,一个传递id值作为正向测试,一个不传id值作为反向测试:

        @Test
        public void testHelloWorldMockMethod() {
            HelloWorld world=new HelloWorld();
            MockHttpServletRequest request=new MockHttpServletRequest();
            MockHttpServletResponse response=new MockHttpServletResponse();
            
            request.setRequestURI("/testMock");
            request.setAttribute("id", 1);
            world.testMock(request, response);
            assertEquals(200, response.getStatus());
            assertEquals(100, (int)request.getAttribute("viewed"));
        }
        
        @Test
        public void testHelloWorldNotAttributeMethod() {
            HelloWorld world=new HelloWorld();
            MockHttpServletRequest request=new MockHttpServletRequest();
            MockHttpServletResponse response=new MockHttpServletResponse();
            
            request.setRequestURI("/testMock");
            world.testMock(request, response);
            assertEquals(500, response.getStatus());
            assertNull(request.getAttribute("viewed"));
        }

    HelloWord就是Controller类,我们先初始化这个Controller,然后,Mock出两个对象,一个MockHttpServlet和一个MockHttpServletResponse。前者用于设置请求URL,并向MockHttpServletRequest添加一个属性“id”。之后调用Controller中的testMock方法,传递这两个Mock对象,最后验证响应状态吗是否为200,请求参数“viewed”的值。

    ModelAndViewAssert

    ModelAndViewAssert类是org.springframework.web.servlet包的一部分,是另一个有用的Spring类,用于测试控制器的请求处理方法返回的ModelAndView。ModelAndView是请求处理方法可以返回的类型之一,是包含有关请求处理方法的模型和视图的一个bean。

    ModelAndViewAssert的一些主要方法:

    方法 描述
    assertViewName 检查ModelAndView的视图名是否与预期名称匹配
    assertModelAttributeAvailable 检查ModelAndView的模型是否包括具有预期模型名称的属性
    assertModelAttributeValue 检查ModelAndView模型是否包含具有指定名称和值的属性
    assertSortAndCompareListModelAttribute 对ModelAndView的列表进行排序,肉厚将其与预期列表进行比较

    例子:

    一个返回ModelAndView的请求处理方法:

        @RequestMapping("/latest/{pubYear}")
        public ModelAndView getLatestTitlees(@PathVariable String pubYear) {
            ModelAndView mv=new ModelAndView("Latest Titles");
            if("2018".equals(pubYear)) {
                List<Book> list=Arrays.asList(
                        new Book("0001", "SpringMVC :A Tutorial", "Paul Deck", LocalDate.of(2018, 1, 1)),
                        new Book("0002", "Java Tutorial", "Budi Hurniawan", LocalDate.of(2018, 1, 16)),
                        new Book("0003", "SQL", "Will Biteman", LocalDate.of(2018, 1, 30))
                        );
                mv.addObject("latest", list);
            }
            return mv;
        }

    其对应的测试方法:

        @Test
        public void test() {
            BookController bookController=new BookController();
            ModelAndView mv=bookController.getLatestTitlees("2018");
            
            ModelAndViewAssert.assertViewName(mv, "Latest Titles");
            ModelAndViewAssert.assertModelAttributeAvailable(mv, "latest");
            
            List<Book> list=Arrays.asList(                    
                    new Book("0001", "SpringMVC :A Tutorial", "Paul Deck", LocalDate.of(2018, 1, 1)),
                    new Book("0002", "Java Tutorial", "Budi Hurniawan", LocalDate.of(2018, 1, 16)),
                    new Book("0003", "SQL", "Will Biteman", LocalDate.of(2018, 1, 30))
                    );
            ModelAndViewAssert.assertAndReturnModelAttributeOfType(mv, "latest", list.getClass());
            Comparator<Book> pubDateComparator=(a,b)->a.getPubDate().compareTo(b.getPubDate());
            ModelAndViewAssert.assertSortAndCompareListModelAttribute(mv, "latest", list, pubDateComparator);
        }

    先是初始化Controller并调用其请求处理方法,这个请求处理方法当然是返回ModelAndView了,我们使用ModelAndViewAssert的静态方法进行测试即可。

    集成测试

    Spring的MockHttpServletRequest,MockHttpServletResponse和ModelAndViewAssert类适用于SpringMVC控制器进行单元测试,但它们缺少与集成测试有关的功能。例如,它们直接调用请求处理方法,无法测试请求映射和数据绑定,它们也不测试bean依赖注入,因为被测试Controller是用new进行实例化。

    所以需要使用新的测试类型和API。MockMvc类位于org.springframework.test.web.servlet包下,是Spring中用于帮助集成测试的。此类允许你使用预定的请求映射来调用请求处理方法。创建MockMvc实例的方法:

    MockMvc mockMvc=MockMvcBuidlers.webAppContextSetip(webAppContext).build();

    这里的webAppContext是WebApplicationContext实例的一个引用,WebApplicationContext是ApplicationContext的一个常用子类。要获得一个WebApplicationContext,你必须在测试类中声明这一点:

    @Autowried
    private WebApplicationContext webAppContext;

    MockMvc其实是一个很简单的类,事实上,它只有一个方法:perform,用于通过URI间接调用SpringMVC控制器。preform方法签名如下:

    ResultActions perform(ResultBuilder requestBuilder)

    要测试请求处理方法,你需要创建一个RequestBuilder。好在可以使用MockMvcRequestBuilders类提供的与HTTP方法具有相同名称的静态方法:get。post,head,put,patch,delete等。要使用HTTP GET方法测试控制器,你可以调用get方法,测试POST调用post方法即可。例如:

    ResultActions resultActions=mockMvc。perform(get("/请求URI"));

    要验证是否成功,需要调用ResultActions的andExpect方法。andExpect方法签名如下:

    ResultAction andExpect(ResultMatcher matcher)

    注意,andExpect方法返回ResultActions的另一个实例,这意味着可以链式调用多个AndExpect方法。该方法的参数为ResultMatcher,MockMvcResultMatchers类提供了静态的方法来轻松创建ResultMatcher.MockMvcResultMatchers属于org.springframework.test.web.servlet.result包。它的部分静态方法如下:

    方法 返回值 描述
    cookie CookieResultMatchers 返回一个ResultMatchers,用于断言cookie值
    header HeaderResultMatchers 返回一个ResultMatchers,用于断言HTTP响应头
    model ModelResultMatchers 返回一个ResultMatchers,用于断言请求处理的模型
    status StatusResultMatchers 返回一个ResultMatchers,用于断言HTTP响应状态
    view ViewResultMatchers 返回一个ResultMatchers,用于断言请求处理的视图

    例如,要保证控制器方法的请求映射正确,可以使用状态方法:

    mockMvc.preform(get('/请求URI').andExpect(status().isOk()));

    isOk方法返回状态码200.

    例子:

    请求处理方法

    package cn.lynu.controller.test;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import cn.lynu.controller.test.domain.Employee;
    import cn.lynu.controller.test.service.EmployeeService;
    
    @Controller
    public class EmployeeController {
        
        @Autowired
        private EmployeeService employeeService;
        
        @RequestMapping("/highest-paid/{category}")
        public String getHighestPaidEmployee(@PathVariable int category,Model model) {
            Employee employee = employeeService.getHighestPaidEmployee(category);
            model.addAttribute("employee", employee);
            return "success";
        }
    
    }

    测试方法

    package cn.lynu.controller.test;
    
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
    import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
    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.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;
    
    /**
     * 集成测试
     * @author lz
     *
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @WebAppConfiguration("/webapp")
    @ContextConfiguration("classpath:test-config.xml")
    public class EmployeeControllerTest {
        
        @Autowired
        private WebApplicationContext webApplicationContext;
        
        private MockMvc mockMvc;
        
        @Before
        public void before() {
            this.mockMvc=MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
        }
        
        @Test
        public void testGetHighestPaidEmployee() {
            try {
                mockMvc.perform(get("/highest-paid/1"))
                        .andExpect(status().isOk())
                        .andExpect(model().attributeExists("employee"))
                        .andDo(print());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    }

    这里的测试开始与单元测试有所不同。首先是测试类运行器。需要一个SpringJUnit4ClassRunner.class在@RunWith注解内:

    @RunWith(SpringJUnit4ClassRunner.class)

    这个runner允许你使用spring。

    然后添加如下注解类型:

    @WebAppConfiguration("/webapp")
    @ContextConfiguration("classpath:test-config.xml")

    WebAppConfiguration注解类型用于声明为集成测试加载的ApplicationContext应该为WebApplicationContext,并指定webapp为项目的根目录,这是一个Maven项目的根目录,如果不是Maven项目就修改为对应项目的根目录,不然可能会出问题。

    ContextConfiguration注解告诉测试运行器加载哪个配置文件,这个test-config.xml就是一个基本的springMVC配置文件。

    除此之外,测试类还需要这两个对象:

        @Autowired
        private WebApplicationContext webApplicationContext;
        
        private MockMvc mockMvc;
    webApplicationContext 会在@before修饰的测试方法中被初始化:
        @Before
        public void before() {
            this.mockMvc=MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
        }

    在测试方法中,我们期望Controller方法响应状态码为200,并且模型具有employee属性。andDo(print())方法的调用将打印各种值。

    其他常用注解

    @RequestHeader

    请求头包含了若干个属性,服务器可据此获知客户端的信 息,通过 @RequestHeader 即可将请求头中的属性值绑定到处理方法的入参中

    这里可以通过给@RequestHeader注解的value指定值来获得所需的请求头信息,也可以不指定,不指定的情况下就去获取参数名对应的请求头信息:

    @CookieValue

    使用@CookieValue 可让处理方法入参绑定某个 Cookie 值

        /**
         * 使用@CookieValue
         */
        @RequestMapping("/testCookieValue")
        public void testCookieValue(@CookieValue(value="sessionId",required=false) String sessionId) {
            System.out.println(sessionId);
        }

    @SessionAttributes

    这个注解是需要放在Controller类上方面的,也只能放在类上。作用是将使用@ModelAttribute修饰的数据放到session中,这样我们放在Model中的数据(根据数据名或数据类型)就会添加进session:

    这样名为name2的,被@ModelAttribute修饰的对象就会被放入session中,但是这个注解存在一些问题:

    springMVC 先在Model中寻找对应key的值,如果没有找到再看handler有没有@SessionAttributes,如果有这个注解,如果有就从session中寻找,没找到就会抛这个异常;没有这个注解才创建一个新的POJO也没有异常。也就说该注解可能会触发一个异常

    所以,如果需要将数据放在session中,我们还是使用HttpSession作为请求处理方法的形参这种方式吧。HttpSession是可以作为SpringMVC方法的形参的。

    mvc-view-controller 配置

    我们都知道将页面放到WEB-INF下,就不能直接访问到,只能通过程序访问到,这里面的页面相互之间也是访问不到的(通过超链接),所以如果希望还可以直接访问WEB-INF下的页面,需要在SpringMVC配置文件中配置 mvc-view-controller ,这样访问就不走控制器了,但是需要注意的是该配置需要 annotation-driver 配置的支持(这一点同配置 default-servlet-handler  一样),否则,控制器里的所有请求处理方法都失效:

  • 相关阅读:
    .NET 正则表达式使用高级技巧之替换类介绍
    道法术器势
    JS函数匿名替换
    批量更改数据库表架构(生成sql后直接执行!)
    转: 从现实生活中理解什么是广播机制
    public View getView(int position, View convertView, final ViewGroup parent)三个参数的意思
    Android Intent个人介绍
    WPF中Timer与DispatcherTimer类的区别
    C# 使用ManualResetEvent 进行线程同步
    C# 使用AutoResetEvent进行线程同步
  • 原文地址:https://www.cnblogs.com/lz2017/p/8392543.html
Copyright © 2011-2022 走看看