Spring入門(十四):Spring MVC控制器的2種測試方法
作為一名研發人員,不管你願不願意對自己的程式碼進行測試,都得承認測試對於研發質量保證的重要性,這也就是為什麼每個公司的技術部都需要質量控制部的原因,因為越早的發現程式碼的bug,成本越低,比如說,Dev環境發現bug的成本要低於QA環境,QA環境發現bug的成本要低於Prod環境,Prod環境發現bug的成本最高,這也是每個研發人員最不願意遇到但永遠避不掉的現實。
雖然不能完全避免,但我們可以對自己的程式碼進行充分的測試,降低bug出現的機率。
所以, 本篇部落格我們主要講解下Spring MVC控制器的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個目錄:
- src/main/java:專案程式碼
- src/main/resources:專案資源
- src/test/java:測試程式碼
- 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”. 但自己的配置文件中必須配