返回介绍

13.5 对 Spring MVC Controller 单元测试

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

你已经学习了如何在 Spring MVC 应用程序中测试各个类。但 Controller 有点不同,因为它们通常与 Servlet API 对象(如 HttpServletRequest、HttpServletResponse、HttpSession 等)交互。在许多情况下,你将需要模拟这些对象以正确测试控制器。

像 Mockito 或 EasyMock 这样的框架是可以模拟任何 Java 对象的通用模拟框架。你必须自己配置生成的对象(使用一系列 when 语句)。Spring-Test 模拟对象是专门为使用 Spring 而构建的,并且与真实对象更接近,更容易使用。

以下小节讨论了一些更重要的单元测试控制器类型。

13.5.1 MockHttpServletRequest 和 MockHttpServletResponse

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

这两个类很容易使用。你可以通过调用其无参构造函数来创建实例:

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

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

表 13.1 MockHttpServletRequest 的主要方法

方法

描述

addHeader

添加一个 HTTP 请求头

addParameter

添加一个请求参数

getAttribute

返回一个属性

getAttributeNames

返回包含了全部属性名的一个 Enumeration 对象

getContextPath

返回上下文路径

getCookies

返回全部的 cookies

setMethod

设置 HTTP 方法

setParameter

设置一个参数值

setQueryString

设置查询语句

setRequestURI

设置请求 URI

MockHttpServletResponse 实现了 javax.servlet.http.HttpServletResponse,并提供了配置实例的其他方法。表 13.2 显示了其一些主要的方法。

表 13.2 MockHttpServletResponse 的主要方法

方法

描述

addCookie

添加一个 cookie

addHeader

添加一个 HTTP 响应头

getContentLength

返回内容长度

getWriter

返回 Writer

getOutputStream

返回 ServletOutputStream

考虑清单 13.21 的 VideoController 类。

清单 13.21 VideoController 类

package com.example.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; 

@Controller
public class VideoController { 
  @RequestMapping(value = "/mostViewed")
  public String getMostViewed(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 "mostViewed"; 
  } 
}

VideoController 类的 getMostViewed 方法中,若请求属性 id 存在且值为 1 或 2,则添加请求属性“viewed”。否则,不添加请求属性。

清单 13.22 中的 VideoControllerTest 类使用两个测试方法验证这一点。

清单 13.22 VideoControllerTest 类

package com.example.controller;
import org.junit.Test;
import static org.junit.Assert.*;
import org.springframework.mock.web.MockHttpServletRequest; 
import org.springframework.mock.web.MockHttpServletResponse; 

public class VideoControllerTest { 
  @Test 
  public void testGetMostViewed() {
    VideoController videoController = new VideoController(); 
    MockHttpServletRequest request = new MockHttpServletRequest(); 
    request.setRequestURI("/mostViewed"); 
    request.setAttribute("id", 1);
    MockHttpServletResponse response = new MockHttpServletResponse(); 

    videoController.getMostViewed(request, response); 
    assertEquals(200, response.getStatus()); 
    assertEquals(100L, (int) request.getAttribute("viewed")); 
  } 

  @Test
  public void testGetMostViewedWithNoId() { 
    VideoController videoController = new VideoController();
    MockHttpServletRequest request = new MockHttpServletRequest(); 
    request.setRequestURI("/mostViewed");
    MockHttpServletResponse response = new MockHttpServletResponse(); 

    videoController.getMostViewed(request, response); 
    assertEquals(500, response.getStatus());
    assertNull(request.getAttribute("viewed")); 
  } 
}

testGetMostViewed 方法实例化 VideoController 类并创建两个 mock 对象,一个 MockHttp ServletRequest 和一个 MockHttpServletResponse。它还设置请求 URI,并向 MockHttpServletRequest 添加属性“id”。

VideoController videoController = new VideoController();
MockHttpServletRequest request = new MockHttpServletRequest(); 
request.setRequestURI("/mostViewed");
request.setAttribute("id", 1);
MockHttpServletResponse response = new MockHttpServletResponse();

然后调用 VideoController 的 getMostView 方法,传递 mock 对象,然后验证响应的状态码为 200,请求包含一个值为 100 的“viewed”属性。

videoController.getMostViewed(request, response); 
assertEquals(200, response.getStatus()); 
assertEquals(100L, (int) request.getAttribute("viewed"));

VideoControllerTest 中的第二个方法类似方法一,但不会向 MockHttpServletRequest 对象添加“id”属性。因此,在调用控制器的方法时,它接收 HTTP 响应状态代码 500,并且在 MockHttpServletRequest 对象中没有“viewed”属性。

13.5.2 ModelAndViewAssert

ModelAndViewAssert 类是 org.springframework.web.servlet 包的一部分,是另一个有用的 Spring 类,用于测试从控制器的请求处理方法返回的 ModelAndView。回想一下第 4 章中介绍过,ModelAndView 是请求处理方法可以返回的类型之一,是包含有关请求处理方法的模型和视图的信息的一个 bean。

表 13.3 列举了 ModelAndViewAssert 的一些主要方法。

表 13.3 ModelAndViewAssert 的主要方法

方法

描述

assertViewName

检查 ModelAndView 的视图名称是否与预期名称匹配

assertModelAttributeAvailable

检查 ModelAndView 的模型是否包含具有预期模型名称的属性

assertModelAttributeValue

检查 ModelAndView 模型是否包含具有指定名称和值的属性

assertSortAndCompareList-ModelAttribute

对 ModelAndView 的列表排序,然后将其与预期列表进行比较

例如,考虑清单 13.23 中的 Book 类。这是一个简单的 bean 类,有 4 个属性:isbn、title、author 和 pubDate

清单 13.23 Book 类

package com.example.model; 
import java.time.LocalDate; 

public class Book {
  private String isbn; 
  private String title; 
  private String author; 
  private LocalDate pubDate; 

  public Book(String isbn, LocalDate pubDate) { 
    this.isbn = isbn;
    this.pubDate = pubDate; 
  } 

  public Book(String isbn, String title, String author, 
      LocalDate pubDate) { 
    this.isbn = isbn; 
    this.title = title; 
    this.author = author; 
    this.pubDate = pubDate; 
  }

  // getters and setters not shown to save space 

  @Override
  public boolean equals(Object otherBook) { 
    return isbn.equals(((Book)otherBook).getIsbn()); 
  } 
}

清单 13.24 中的 BookController 类是一个 Spring MVC 控制器,它包含一个请求处理方法 getLatestTitles。该方法接受 pubYear 路径变量,并返回一个 ModelAndView,如果 pubYear 的值为“2016”,它将包含书籍列表。

清单 13.24 BookController 类

package com.example.controller; 
import java.time.LocalDate; 
import java.util.Arrays;
import java.util.List; 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.servlet.ModelAndView;
import com.example.model.Book; 

@Controller
public class BookController { 
  @RequestMapping(value = "/latest/{pubYear}") 
  public ModelAndView getLatestTitles( 
      @PathVariable String pubYear) {
    ModelAndView mav = new ModelAndView("Latest Titles"); 

    if ("2016".equals(pubYear)) { 
      List<Book> list = Arrays.asList( 
          new Book("0001", "Spring MVC: A Tutorial", 
              "Paul Deck", 
              LocalDate.of(2016, 6, 1)), 
          new Book("0002", "Java Tutorial", 
              "Budi Kurniawan", LocalDate.of(2016, 11, 1)), 
          new Book("0003", "SQL", "Will Biteman", 
              LocalDate.of(2016, 12, 12))); 
      mav.getModel().put("latest", list); 
    } 
    return mav; 
  } 
}

测试 BookController 的一种简单方式是使用 ModelAndViewAssert 中的静态方法,如清单 13.25 中的 BookControllerTest 类所示。

清单 13.25 BookControllerTest 类

package com.example.controller;
import static org.springframework.test.web.ModelAndViewAssert.*; 
import java.time.LocalDate;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import org.junit.Test;
import org.springframework.web.servlet.ModelAndView;
import com.example.model.Book; 

public class BookControllerTest { 
  @Test 
  public void test() {
    BookController bookController = new BookController(); 
    ModelAndView mav = bookController.getLatestTitles("2016"); 
    assertViewName(mav, "Latest Titles"); 
    assertModelAttributeAvailable(mav, "latest"); 
    List<Book> expectedList = Arrays.asList( 
        new Book("0002", LocalDate.of(2016, 11, 1)), 
        new Book("0001", LocalDate.of(2016, 6, 1)), 
        new Book("0003", LocalDate.of(2016, 12, 12))); 
    assertAndReturnModelAttributeOfType(mav, "latest", 
        expectedList.getClass()); 
    Comparator<Book> pubDateComparator =
        (a, b) -> a.getPubDate().compareTo(b.getPubDate()); 
    assertSortAndCompareListModelAttribute(mav, "latest",
        expectedList, pubDateComparator); 
  } 
}

发布评论

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