返回介绍

13.6 应用 Spring MVC Test 进行集成测试

发布于 2025-04-22 20:10:05 字数 9923 浏览 0 评论 0 收藏

集成测试用来测试不同的模块是否可以一起工作。它还确保两个模块之间数据的传递。使用 Spring 框架依赖注入容器,必须检查 bean 依赖注入。

若没有合适的工具,集成测试可能需要很多时间。想象一下,如果你正在建立一个网上商店,你必须使用网络浏览来测试购物车是否正确计算。每次更改代码时,你必须启动浏览器,登录系统,将几个项目添加到购物车,并检查总数是否正确。每次迭代可以轻易地花费 5 分钟!

好在,Spring 提供了一个用于集成测试的模块:Spring Test。

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

对于集成测试,您需要一组不同的 Spring MVC 测试类型。以下小节讨论集成测试的 API 并提供一个示例。

13.6.1 API

作为 Spring 的一个模块,Spring MVC Test 提供了一些实用程序类,可以方便地在 Spring MVC 应用程序上执行集成测试。bean 是使用 Spring 依赖注入器创建的,并从 ApplicationContext 中获取,就像在一个真正的 Spring 应用程序中一样。

MockMvc 类位于 org.springframework.test.web.servlet 包下,是 Spring MVC Test 中的主类,用于帮助集成测试。此类允许你使用预定义的请求映射来调用请求处理方法。这里是一种常见的创建 MockMvc 实例的方法:

MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext).build();

这里,webAppContext 是 WebApplicationContext 实例的一个引用,WebApplicationContext 是第 2 章中讨论的 ApplicationContext 的子类,每个 Spring 开发人员都应该熟悉。要获取一个 WebApplicationContext,你必须在测试类中声明这一点。

@Autowired private WebApplicationContext webAppContext;

MockMvcBuilder 读取一个 Spring 配置文件或为测试类定义的文件。我将讨论如何指定测试类的配置文件,但首先我想讨论 MockMvc。

MockMvc 是一个非常简单的类。事实上,它只有一个方法:perform,用于通过 URI 间接调用 Spring MVC 控制器。

perform 方法具有以下签名:

ResultActions perform(RequestBuilder requestBuilder

要测试请求处理方法,你需要创建一个 RequestBuilder。好在,MockMvcRequestBuilders 类提供了与 HTTP 方法具有相同名称的静态方法:get、post、head、put、patch、delete 等。要使用 HTTP GET 方法测试控制器,你可以调用 get 方法,要使用 POST 测试,则调用 post 方法。这些静态方法也很容易使用,你只需要传递一个字符串——控制器的请求处理方法的 URI。

例如,要调用名为 getEmployee 的请求处理方法,你将编写如下代码:

ResultActions resultActions = mockMvc.perform(get(“/ getEmployee”));

当然,你必须导入 MockMvcRequestBuilders 的静态 get 方法。

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

ResultAction andExpect(ResultMatcher matcher

注意,andExpect 返回 ResultActions 的另一个实例,这意味着可以链式调用多个 AndExpect,你可以在稍后的示例中看到这一点。

MockMvcResultMatchers 类提供了静态方法来轻松创建 ResultMatcher。MockMvcResultMatchers 属于 org.springframework.test.web.servlet.result 包。表 13.4 显示了它的一些方法。

表 13.4 MockMvcResultMatchers 的主要方法

方法

返回类型

描述

cookie

CookieResultMatchers

返回一个 ResultMatchers,用来断言 cookie 值

header

HeaderResultMatchers

返回一个 ResultMatchers,用来断言 HTTP 响应头部

model

ModelResultMatchers

返回一个 ResultMatchers,用来断言请求处理的模型

status

StatusResultMatchers

返回一个 ResultMatchers,用来断言 HTTP 响应状态

view

ViewResultMatchers

返回一个 ResultMatchers,用来断言请求处理的视图

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

mockMvc.perform(get("/ getBook")).andExpect(status().isOk());

isOK 方法断言响应状态代码是 200,可以看到 MockMvc 及其相关类使得集成测试控制器变的非常容易。

13.6.2 Spring MVC 测试类的框架

了解了 Spring MVC Test 中的一些重要的 API,现在来看下 Spring MVC 测试类的框架。

import org.junit.After;
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; 

@RunWith(SpringJUnit4ClassRunner.class) 
@WebAppConfiguration 
@ContextConfiguration("...")
public class ProductControllerTest { 
  @Autowired
  private WebApplicationContext webAppContext; 

  private MockMvc mockMvc; 

  @Before
  public void setup() { 
    mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext).build(); 
  } 

  @After 
  public void cleanup(){
  } 

  @Test
  public void test1() throws Exception { 
    mockMvc.perform(...).andExpect(...); 
  }

  @Test
  public void test2() throws Exception { 
    mockMvc.perform(...).adnExpect(...); 
  } 

  ...

}

首先,看下你要导入的类型。在导入列表的顶部,是来自 JUnit 和 Spring MVC Test 的类型。像单元测试类一样,Spring MVC 测试类可以包括用 @Before 和 @After 注解的方法。两种注解类型都是 JUnit 的一部分。

接下去,测试框架开始与单元测试有所不同。首先是测试类运行器。你需要一个 SpringJUnit4ClassRunner.class 在 @RunWith 注解内:

@RunWith(SpringJUnit4ClassRunner.class)

这个 runner 允许你使用 spring。

然后,你需要添加如下注解类型:

@WebAppConfiguration 
@ContextConfiguration("...")

WebAppConfiguration 注解类型用于声明为集成测试加载的 ApplicationContext 应该是 WebApplicationContext 类型。ContextConfiguration 注解类型告诉测试运行器如何加载和配置 WebApplicationContext。

除以上,测试类中还需要两个对象:

private WebApplicationContext webAppContext; 
private MockMvc mockMvc;

13.6.3 示例

以下示例展示如何对 Spring MVC 控制器开展集成测试。

清单 13.26 中的配置文件展示了将被扫描的包。这个文件是一个典型的 Spring MVC 配置文件,但减去了任何资源映射和视图解析器。但是,你可以使用实际的配置文件。

清单 13.26 test-config.xml 文件

< ?xml version="1.0" encoding="UTF-8"?>
< beans xmlns="http://www.springframework.org/schema/beans" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:p="http://www.springframework.org/schema/p" 
  xmlns:mvc="http://www.springframework.org/schema/mvc" 
  xmlns:context="http://www.springframework.org/schema/context" 
  xsi:schemaLocation=" 
   http://www.springframework.org/schema/beans 
   http://www.springframework.org/schema/beans/spring-beans.xsd 
   http://www.springframework.org/schema/mvc 
   http://www.springframework.org/schema/mvc/spring-mvc.xsd 
   http://www.springframework.org/schema/context 
   http://www.springframework.org/schema/context/spring-context.xsd"> 
  < context:component-scan base-package="controller"/>
  < context:component-scan base-package="service"/> 
  < mvc:annotation-driven/> 
< /beans> 

此示例中的 SUT 是清单 13.27 中的 EmployeeController 类。类中只有一个请求处理方法,getHighestPaid,它映射到/highest-paid。

清单 13.27 EmployeeController 类

package controller;
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 service.EmployeeService;
import domain.Employee; 

@Controller
public class EmployeeController { 

  @Autowired
  EmployeeService employeeService; 

  @RequestMapping(value="/highest-paid/{category}")
  public String getHighestPaid(@PathVariable int category, Model model) 
  { 
    Employee employee = employeeService.getHighestPaidEmployee( category); 
    model.addAttribute("employee", employee); 
    return "success"; 
  } 
}

清单 13.28 展示了 EmployeeController 的测试类。

清单 13.28 EmployeeControllerTest 类

package com.example.controller;
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.After;
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; 

@RunWith(SpringJUnit4ClassRunner.class) 
@WebAppConfiguration 
@ContextConfiguration("test-config.xml") 
public class EmployeeControllerTest { 
  @Autowired
  private WebApplicationContext webAppContext; 

  private MockMvc mockMvc; 

  @Before
  public void setup() { 
    this.mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
        .build(); 
  } 

  @After
  public void cleanUp() { 

  } 

  @Test
  public void testGetHighestPaidEmployee() throws Exception { 
    mockMvc.perform(get("/highest-paid/2"))
        .andExpect(status().isOk()) 
        .andExpect(model().attributeExists("employee"))
        .andDo(print()); 
  }
} 

EmployeeControllerTest 类包含一个 setUp 方法,它创建一个 MockMvc 对象。 testGetHighestPaidEmployee 方法执行测试,并期望响应状态代码为 200,并且模型具有 employee 属性。

测试方法还调用 andDo(print()) 在响应对象中打印各种值。如果你的测试成功通过,你应该会看到类似的结果。

MockHttpServletRequest:
   HTTP Method = GET
   Request URI = /highest-paid/2 
   Parameters = {}
     Headers = {} 
Handler: 
      Type = controller.EmployeeController 
     Method = public java.lang.String 
   controller.EmployeeController.getHighestPaid(int,org.springframe-
   work.ui.Model) 

Async:
  Async started = false 
   Async result = null 

Resolved Exception:
       Type = null 

ModelAndView:
    View name = success 
       View = null
    Attribute = employee 
      value = Xiao Ming ($200000)
      errors = [] 

FlashMap:
    Attributes = null 

MockHttpServletResponse: 
      Status = 200 
  Error message = null
     Headers = {} 
   Content type = null 
       Body = 
  Forwarded URL = success 
  Redirected URL = null 
     Cookies = []

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。