1. 程式人生 > >Spring入門(十四):Spring MVC控制器的2種測試方法

Spring入門(十四):Spring MVC控制器的2種測試方法

作為一名研發人員,不管你願不願意對自己的程式碼進行測試,都得承認測試對於研發質量保證的重要性,這也就是為什麼每個公司的技術部都需要質量控制部的原因,因為越早的發現程式碼的bug,成本越低,比如說,Dev環境發現bug的成本要低於QA環境,QA環境發現bug的成本要低於Prod環境,Prod環境發現bug的成本最高,這也是每個研發人員最不願意遇到但永遠避不掉的現實。

雖然不能完全避免,但我們可以對自己的程式碼進行充分的測試,降低bug出現的機率。

所以, 本篇部落格我們主要講解下Spring MVC控制器的2種測試方法:

  1. 部署專案後測試
  2. 藉助JUnit和Spring Test框架測試

1. 部署專案後測試

在前2篇部落格中,我們採取的就是這種測試方式,即將專案打成war包,部署到Tomcat中,執行專案後, 藉助瀏覽器或者Postman等工具對控制器進行測試。

如果是get請求,可以使用瀏覽器或者Postman測試。

如果是post、put、delete等請求,可以使用Postman進行測試。

有興趣的同學,可以看下之前的2篇部落格:

Spring入門(十二):Spring MVC使用講解

Spring入門(十三):Spring MVC常用註解講解

2. 藉助Junit和Spring Test框架測試

上面的方法雖然可以進行測試,但每次都打包、部署、執行專案、測試,顯然很不方便,不過強大的Spring通過Spring Test框架對整合測試提供了支援,接下來我們講解具體的使用方法。

因為我們的Spring專案是通過maven管理的,所以它的專案結構有以下4個目錄:

  1. src/main/java:專案程式碼
  2. src/main/resources:專案資源
  3. src/test/java:測試程式碼
  4. src/test/resources:測試資源(該目錄預設沒有生成,有需要的可以自己新建)

也就是說,我們可以將我們的測試程式碼放在src/test/java目錄下,不過截止目前,我們還並未在該目錄新增任何測試程式碼。

2.1 新增依賴

在新增測試程式碼前,我們需要在pom.xml中新增如下依賴:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>4.3.18.RELEASE</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

也許有的同學會好奇,為啥本次新增的依賴增加了<scope>test</scope>, 它有啥作用呢?

帶著這個疑問,我們編譯下專案,發現原本編譯正常的程式碼竟然編譯報錯了:

報錯資訊提示程式包org.junit不存在,可我們明明添加了該依賴啊,這是為什麼呢,會不會和<scope>test</scope>有關呢?

恭喜你,猜對了,確實和<scope>test</scope>有關,如果你此時將該項移除,專案編譯就不報錯了(不過建議不要移除)。

這是因為,我們在之前新增測試程式碼時,都是放在src/main/java目錄下的,現在依賴包增加了<scope>test</scope>,說明這些包的存活週期是在test週期,所以我們可以把之前的測試程式碼移到src/test/java目錄下,如下所示:

再次編譯專案,發現編譯通過。

2.2 新增控制器

新增控制器前,新建DemoService如下所示:

package chapter05.service;

import org.springframework.stereotype.Service;

@Service
public class DemoService {
    public String saySomething() {
        return "hello";
    }
}

注意事項:該類添加了@Service註解。

然後,新建控制器NormalController,它裡面的方法返回jsp檢視:

package chapter05.controller;

import chapter05.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class NormalController {
    @Autowired
    private DemoService demoService;

    @RequestMapping("/normal")
    public String testPage(Model model) {
        model.addAttribute("msg", demoService.saySomething());
        return "page";
    }
}

接著新建控制器MyRestController,它裡面的方法直接返回資訊:

package chapter05.controller;

import chapter05.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyRestController {
    @Autowired
    private DemoService demoService;

    @RequestMapping(value = "/testRest", produces = "text/plain;charset=UTF-8")
    public String testRest() {
        return demoService.saySomething();
    }
}

2.3 新增測試程式碼

在src/test/java下新建包chapter05,然後在其下面新建測試類TestControllerIntegrationTests如下所示:

package chapter05;

import chapter05.config.MyMvcConfig;
import chapter05.service.DemoService;
import org.junit.Before;
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)
@ContextConfiguration(classes = {MyMvcConfig.class})
@WebAppConfiguration("src/main/resources")
public class TestControllerIntegrationTests {
    private MockMvc mockMvc;

    @Autowired
    private DemoService demoService;

    @Autowired
    private WebApplicationContext webApplicationContext;

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

程式碼講解:

@RunWith(SpringJUnit4ClassRunner.class)用於在JUnit環境下提供Spring Test框架的功能。

@ContextConfiguration(classes = {MyMvcConfig.class})用來載入配置ApplicationContext,其中classes屬性用來載入配置類,MyMvcConfig配置類的程式碼如下所示:

package chapter05.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

/**
 * Spring MVC配置
 */
@Configuration
@EnableWebMvc
@ComponentScan("chapter05")
public class MyMvcConfig {
    /**
     * 檢視解析器配置
     *
     * @return
     */
    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();

        viewResolver.setPrefix("/WEB-INF/classes/views/");
        viewResolver.setSuffix(".jsp");
        viewResolver.setViewClass(JstlView.class);

        return viewResolver;
    }
}

@WebAppConfiguration("src/main/resources") 用來宣告載入的ApplicationContext是一個WebApplicationContext,它的屬性指定的是Web資源的位置,預設為src/main/webapp,這裡我們修改成

src/main/resources。

MockMvc用來模擬Mvc物件,它在添加了@Before註解的setup()中,通過this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build();進行初始化賦值。

然後往測試類中新增如下測試程式碼:

@Test
public void testNormalController() throws Exception {
    mockMvc.perform(get("/normal"))
            .andExpect(status().isOk())
            .andExpect(view().name("page"))
            .andExpect(forwardedUrl("/WEB-INF/classes/views/page.jsp"))
            .andExpect(model().attribute("msg", demoService.saySomething()));
}

程式碼解釋:

perform(get("/normal"))用來模擬向/normal發起get請求,

andExpect(status().isOk())預期返回的狀態碼為200,

andExpect(view().name("page"))預期檢視的邏輯名稱為page,

andExpect(forwardedUrl("/WEB-INF/classes/views/page.jsp"))預期檢視的真正路徑是/WEB-INF/classes/views/page.jsp",

andExpect(model().attribute("msg", demoService.saySomething()))預期Model裡有一個msg屬性,它的值是demoService.saySomething()的返回值hello。

執行該測試方法,測試通過:

最後往測試類中新增如下測試程式碼:

@Test
public void testRestController() throws Exception {
    mockMvc.perform(get("/testRest"))
            .andExpect(status().isOk())
            .andExpect(content().contentType("text/plain;charset=UTF-8"))
            .andExpect(content().string(demoService.saySomething()));
}

程式碼解釋:

perform(get("/testRest"))用來模擬向/testRest發起get請求,

andExpect(status().isOk())預期返回的狀態碼為200,

andExpect(content().contentType("text/plain;charset=UTF-8"))預期返回值的媒體型別為text/plain;charset=UTF-8,

andExpect(content().string(demoService.saySomething()))預期返回值的內容為demoService.saySomething()的返回值hello。

執行該測試方法,測試通過:

3. 原始碼及參考

原始碼地址:https://github.com/zwwhnly/spring-action.git,歡迎下載。

Craig Walls 《Spring實戰(第4版)》

汪雲飛《Java EE開發的顛覆者:Spring Boot實戰》

4. 最後

歡迎掃碼關注微信公眾號:「申城異鄉人」,定期分享Java技術乾貨,讓我們一起進步。

相關推薦

Spring入門()Spring MVC控制器2測試方法

作為一名研發人員,不管你願不願意對自己的程式碼進行測試,都得承認測試對於研發質量保證的重要性,這也就是為什麼每個公司的技術部都需要質量控制部的原因,因為越早的發現程式碼的bug,成本越低,比如說,Dev環境發現bug的成本要低於QA環境,QA環境發現bug的成本要低於Prod環境,Prod環境發現bug的成本

Spring入門(二)Spring MVC使用講解

1. Spring MVC介紹 提到MVC,參與過Web應用程式開發的同學都很熟悉,它是展現層(也可以理解成直接展現給使用者的那一層)開發的一種架構模式,M全稱是Model,指的是資料模型,V全稱是View,指的是檢視頁面,如JSP、Thymeleaf等,C全稱是Controller,指的是控制器,用來處理使

Spring Cloud ()Spring Cloud 開源軟體都有哪些?

學習一門新的技術如果有優秀的開源專案,對初學者的學習將會是事半功倍,通過研究和學習優秀的開源專案,可以快速的瞭解此技術的相關應用場景和應用示例,參考優秀開源專案會降低將此技術引入到專案中的成本。為此抽了一些時間為大家尋找了一些非常優秀的 Spring Cloud 開源軟體供大家學習參考。 上次寫了一篇文章Sp

Spring入門(一)Spring AOP使用進階

在上篇部落格中,我們瞭解了什麼是AOP以及在Spring中如何使用AOP,本篇部落格繼續深入講解下AOP的高階用法。 1. 宣告帶引數的切點 假設我們有一個介面CompactDisc和它的實現類BlankDisc: package chapter04.soundsystem; /** * 光碟 */ p

Spring Cloud 之Hystrix Dashboard結合Turbine

1. 簡介 Turbine (provided by the Spring Cloud Netflix project), aggregates multiple instances Hystrix

Spring Security原始碼分析Spring Social 社交登入的繫結與解綁

社交登入又稱作社會化登入(Social Login),是指網站的使用者可以使用騰訊QQ、人人網、開心網、新浪微博、搜狐微博、騰訊微博、淘寶、豆瓣、MSN、Google等社會化媒體賬號登入該網站。 前言 在之前的Spring Social系列中,我

Spring入門(五)使用Spring JDBC操作資料庫

在本系列的之前部落格中,我們從沒有講解過操作資料庫的方法,但是在實際的工作中,幾乎所有的系統都離不開資料的持久化,所以掌握操作資料庫的使用方法就非常重要。 在Spring中,操作資料庫有很多種方法,我們可以使用JDBC、Hibernate、MyBatis或者其他的資料持久化框架,本篇部落格的重點是講解下在Sp

spring學習筆記spring常用註解總結

bean logs single 配置文件 屬性註入 ring 如果 let ons 使用spring的註解,需要在配置文件中配置組件掃描器,用於在指定的包中掃描註解 <context:component-scan base-package="xxx.xxx.xxx

Spring Boot(一)Spring Boot中MongoDB的使用

Spring Boot(十一):Spring Boot中MongoDB的使用 mongodb是最早熱門非關係資料庫的之一,使用也比較普遍,一般會用做離線資料分析來使用,放到內網的居多。由於很多公司使用了雲服務,伺服器預設都開放了外網地址,導致前一陣子大批 MongoDB 因配置漏洞被攻擊,資料被刪,引起了人

Spring Boot(五)spring boot+jpa+thymeleaf增刪改查示例

app 配置文件 quest 重啟 fin nbu 生產 prot html Spring Boot(十五):spring boot+jpa+thymeleaf增刪改查示例 一、快速上手 1,配置文件 (1)pom包配置 pom包裏面添加jpa和thymeleaf的相關包引

《partner4java 講述Spring入門》之spring cache支援(spring3.1如何使用cache 快取)

(以下內容參照自官方文件;p4jorm下載地址http://blog.csdn.net/partner4java/article/details/8629578;cache demo下載地址http://download.csdn.net/detail/partner4ja

Spring MVC框架之傳統增刪改查06

傳統CRUD 列表頁面: 新增頁面: 編輯頁面: 刪除操作: 匯入SpringMVC jar包 commons-logging-1.1.3.jar spring-aop-4.0.0.RELEASE.jar spring-beans-4.0.0.RELEAS

Spring MVC框架之多IOC容器整合15

多IOC容器整合 SSM整合方式 Spring、SpringMVC、MyBatis SpringMVC的核心Servlet會啟動一個IOC容器,而ContextLoaderListener也會啟動一個IOC容器。 web.xml <?xml version="1.

Spring MVC框架之資料校驗14

第十二章 資料校驗 在Web應用三層架構體系中,表述層負責接收瀏覽器提交的資料,業務邏輯層負責資料的處理。為了能夠讓業務邏輯層基於正確的資料進行處理,我們需要在表述層對資料進行檢查,將錯誤的資料隔絕在業務邏輯層之外。 1.校驗概述 JSR 303是Java為Bean資料合法性

Spring MVC框架之型別轉換13

SpringMVC將“把請求引數注入到POJO物件”這個操作稱為“資料繫結”。 資料型別的轉換和格式化就發生在資料繫結的過程中。 型別轉換和格式化是密不可分的兩個過程,很多帶格式的資料必須明確指定格式之後才可以進行型別轉換。 最典型的就是日期型別。 1.使用SpringMVC內建的型

Spring MVC框架之執行原理12

第十章 SpringMVC執行原理 找到一篇寫的不錯的部落格,大家可以看看 第一節 幾個重要元件 1.HandlerMapping 代表請求地址到handler之間的對映。 2.HandlerExecutionChain handler的執行鏈物件,由handler物件和所有ha

Spring MVC框架之Ajax11

第九章 Ajax Ajax程式和伺服器資料傳輸 在進行Ajax操作時,SpringMVC會需要將JSON資料和Java實體類進行相互轉換,為了實現這個效果需要額外加入jackson-all-1.9.11.jar 1.從瀏覽器傳送資料給handler方法 1請求引數分散提交 頁面

Spring MVC框架之攔截器10

第八章 攔截器 攔截器最典型的用法是檢查使用者是否登入,登入後可以執行目標handler方法,未登入則跳轉到登入頁面。這樣的操作要是在每個攔截器內部來寫就太麻煩了,統一提取到攔截器中是明智之舉。 1.HandlerInterceptor介面 ①preHandle()方法 簽名:b

Spring MVC框架之細節瞭解16

第十四章 瞭解內容 1.SpringMVC配置檔案可以放在WEB-INF下 ①命名規範:[servlet-name]-servlet.xml ②位置:/WEB-INF目錄下 ③示例:/WEB-INF/springDispatcherServlet-servlet.xml ④使用預設配

Spring Cloud 入門教程() 分布式環境下自動發現配置服務

.html article png discover ice conf label tail 註釋 前一章, 我們的Hello world應用服務,通過配置服務器Config Server獲取到了我們配置的hello信息“hello world”. 但自己的配置文件中必須配