SpringBoot 項目單元測試也很方便, Web項目中單元測試應該覆蓋:
1. Service 層
2. Controller 層
本文前半部分講解是一些測試基礎配置. 對於Service和Controller測試的講解,摘自一個博客(嘟嘟獨立博客的博客, Spring Boot干貨系列:(十二)Spring Boot使用單元測試, 鏈接: http://tengj.top/2017/12/28/springboot12/)
=====================
pom.xml 文件
=====================
單元測試包名稱是spring-boot-starter-test, 另外,必須加上 spring-boot-starter-parent.
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.2.RELEASE</version> </parent>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
=====================
java 代碼
=====================
Eclipse 中創建一個Mavan project, 就會自動創建單元測試專用folder, 以及一個AppTest.java, 注意該測試java程序雖然和應用程序入口的java文件不在一個目錄下, 但它們是同一個package.
下面是一個極簡的測試java.
// AppTest.java file @RunWith(SpringRunner.class) @SpringBootTest(classes = App.class) public class AppTest { @Test public void test1() { System.out.println("test1"); } }
上面代碼重點是, 測試類加@RunWith注解, 還有加上 @SpringBootTest(classes = App.class) 注解, 這里的 App.class 是主程序java類. 主程序java程序必須是SpringBootApplication程序, 否則測試用例會報如下錯誤:
Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test java.lang.IllegalStateException.
@RunWith是JUnit的一個注解, 用來告訴JUnit不要使用內置的方式進行單元測試, 而應該使用指定的類做單元測試 對於Spring單元測試總是要使用 SpringRunner.class .
@SpringBootTest 用來指定SpringBoot應用程序的入口類, 該注解默認會根據包名逐級往上找, 一直找到一個SpringBoot主程序class為止, 然后啟動該類為單元測試准備Spring上下文環境. Spring單元測試並不在每個測試方法前都移動一個全新的Spring上下文, 因為這樣做太耗費時間, 而是會緩存上下文環境. 如果某個測試方法需要重新准備Spring上下文, 需要在該方法上加 @DirtiesContext 注解.
@Test注解: JUnit在執行每個測試方法之前, 都會為測試類創建一個新的實例, 這樣有助於隔離各個測試方法之前的相互影響.
下面是一個極簡的主程序java.
// App.java file @SpringBootApplication public class App extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(App.class); } public static void main(String[] args) throws Exception { SpringApplication.run(App.class, args); } }
===========================
下面為轉載內容, 摘自 http://tengj.top/2017/12/28/springboot12/
===========================
Service單元測試
編寫創建好的測試類,具體代碼如下:
1 |
package com.dudu.service; |
上面就是最簡單的單元測試寫法,頂部只要@RunWith(SpringRunner.class)
和SpringBootTest
即可,想要執行的時候,鼠標放在對應的方法,右鍵選擇run該方法即可。
測試用例中我使用了assertThat斷言,下文中會介紹,也推薦大家使用該斷言。
Controller單元測試
上面只是針對Service層做測試,但是有時候需要對Controller層(API)做測試,這時候就得用到MockMvc了,你可以不必啟動工程就能測試這些接口。
MockMvc實現了對Http請求的模擬,能夠直接使用網絡的形式,轉換到Controller的調用,這樣可以使得測試速度快、不依賴網絡環境,而且提供了一套驗證的工具,這樣可以使得請求的驗證統一而且很方便。
Controller類:
1 |
package com.dudu.controller; |
這里我們也自動創建一個Controller的測試類,具體代碼如下:
1 |
package com.dudu.controller; |
上面實現了基本的增刪改查的測試用例,使用MockMvc的時候需要先用MockMvcBuilders使用構建MockMvc對象,如下
1 |
@Before |
因為攔截器那邊會判斷是否登錄,所以這里我注入了一個用戶,你也可以直接修改攔截器取消驗證用戶登錄,先測試完再開啟。
這里拿一個例子來介紹一下MockMvc簡單的方法
1 |
/** |
- mockMvc.perform執行一個請求
- MockMvcRequestBuilders.get(“/user/1”)構造一個請求,Post請求就用.post方法
- contentType(MediaType.APPLICATION_JSON_UTF8)代表發送端發送的數據格式是
application/json;charset=UTF-8
- accept(MediaType.APPLICATION_JSON_UTF8)代表客戶端希望接受的數據類型為
application/json;charset=UTF-8
- session(session)注入一個session,這樣攔截器才可以通過
- ResultActions.andExpect添加執行完成后的斷言
- ResultActions.andExpect(MockMvcResultMatchers.status().isOk())方法看請求的狀態響應碼是否為200如果不是則拋異常,測試不通過
- andExpect(MockMvcResultMatchers.jsonPath(“$.author”).value(“嘟嘟MD獨立博客”))這里jsonPath用來獲取author字段比對是否為
嘟嘟MD獨立博客
,不是就測試不通過 - ResultActions.andDo添加一個結果處理器,表示要對結果做點什么事情,比如此處使用MockMvcResultHandlers.print()輸出整個響應結果信息
mockMvc 更多例子可以本篇下方參考查看
新斷言assertThat使用
JUnit 4.4 結合 Hamcrest 提供了一個全新的斷言語法——assertThat。程序員可以只使用 assertThat 一個斷言語句,結合 Hamcrest 提供的匹配符,就可以表達全部的測試思想,我們引入的版本是Junit4.12所以支持assertThat。
assertThat 的基本語法如下:
清單 1 assertThat 基本語法
1 |
assertThat( [value], [matcher statement] ); |
- value 是接下來想要測試的變量值;
- matcher statement 是使用 Hamcrest 匹配符來表達的對前面變量所期望的值的聲明,如果 value 值與 matcher statement 所表達的期望值相符,則測試成功,否則測試失敗。
assertThat 的優點
- 優點 1:以前 JUnit 提供了很多的 assertion 語句,如:assertEquals,assertNotSame,assertFalse,assertTrue,assertNotNull,assertNull 等,現在有了 JUnit 4.4,一條 assertThat 即可以替代所有的 assertion 語句,這樣可以在所有的單元測試中只使用一個斷言方法,使得編寫測試用例變得簡單,代碼風格變得統一,測試代碼也更容易維護。
- 優點 2:assertThat 使用了 Hamcrest 的 Matcher 匹配符,用戶可以使用匹配符規定的匹配准則精確的指定一些想設定滿足的條件,具有很強的易讀性,而且使用起來更加靈活。如清單 2 所示:
清單 2 使用匹配符 Matcher 和不使用之間的比較
1 |
// 想判斷某個字符串 s 是否含有子字符串 "developer" 或 "Works" 中間的一個 |
-
優點 3:assertThat 不再像 assertEquals 那樣,使用比較難懂的“謂賓主”語法模式(如:assertEquals(3, x);),相反,assertThat 使用了類似於“主謂賓”的易讀語法模式(如:assertThat(x,is(3));),使得代碼更加直觀、易讀。
-
優點 4:可以將這些 Matcher 匹配符聯合起來靈活使用,達到更多目的。如清單 3 所示:
清單 3 Matcher 匹配符聯合使用
1 |
// 聯合匹配符not和equalTo表示“不等於” |
- 優點 5:錯誤信息更加易懂、可讀且具有描述性(descriptive)
JUnit 4.4 以前的版本默認出錯后不會拋出額外提示信息,如:
1 |
assertTrue( s.indexOf("developer") > -1 || s.indexOf("Works") > -1 ); |
如果該斷言出錯,只會拋出無用的錯誤信息,如:junit.framework.AssertionFailedError:null。
如果想在出錯時想打印出一些有用的提示信息,必須得程序員另外手動寫,如:
1 |
assertTrue( "Expected a string containing 'developer' or 'Works'", |
非常的不方便,而且需要額外代碼。
JUnit 4.4 會默認自動提供一些可讀的描述信息,如清單 4 所示:
清單 4 JUnit 4.4 默認提供一些可讀的描述性錯誤信息
1 |
String s = "hello world!"; |
如何使用 assertThat
JUnit 4.4 自帶了一些 Hamcrest 的匹配符 Matcher,但是只有有限的幾個,在類 org.hamcrest.CoreMatchers 中定義,要想使用他們,必須導入包 org.hamcrest.CoreMatchers.*。
清單 5 列舉了大部分 assertThat 的使用例子:
1 |
字符相關匹配符 |
單元測試回滾
單元個測試的時候如果不想造成垃圾數據,可以開啟事物功能,記在方法或者類頭部添加@Transactional
注解即可,如下:
1 |
@Test |
這樣測試完數據就會回滾了,不會造成垃圾數據。如果你想關閉回滾,只要加上@Rollback(false)
注解即可。@Rollback
表示事務執行完回滾,支持傳入一個參數value,默認true即回滾,false不回滾。
如果你使用的數據庫是Mysql,有時候會發現加了注解@Transactional
也不會回滾,那么你就要查看一下你的默認引擎是不是InnoDB,如果不是就要改成InnoDB。
MyISAM與InnoDB是mysql目前比較常用的兩個數據庫存儲引擎,MyISAM與InnoDB的主要的不同點在於性能和事務控制上。這里簡單的介紹一下兩者間的區別和轉換方法:
- MyISAM:MyISAM是MySQL5.5之前版本默認的數據庫存儲引擎。MYISAM提供高速存儲和檢索,以及全文搜索能力,適合數據倉庫等查詢頻繁的應用。但不支持事務、也不支持外鍵。MyISAM格式的一個重要缺陷就是不能在表損壞后恢復數據。
-
InnoDB:InnoDB是MySQL5.5版本的默認數據庫存儲引擎,不過InnoDB已被Oracle收購,MySQL自行開發的新存儲引擎Falcon將在MySQL6.0版本引進。InnoDB具有提交、回滾和崩潰恢復能力的事務安全。但是比起MyISAM存儲引擎,InnoDB寫的處理效率差一些並且會占用更多的磁盤空間以保留數據和索引。盡管如此,但是InnoDB包括了對事務處理和外來鍵的支持,這兩點都是MyISAM引擎所沒有的。
-
MyISAM適合:(1)做很多count 的計算;(2)插入不頻繁,查詢非常頻繁;(3)沒有事務。
- InnoDB適合:(1)可靠性要求比較高,或者要求事務;(2)表更新和查詢都相當的頻繁,並且表鎖定的機會比較大的情況。(4)性能較好的服務器,比如單獨的數據庫服務器,像阿里雲的關系型數據庫RDS就推薦使用InnoDB引擎。
修改默認引擎的步驟
查看MySQL當前默認的存儲引擎:
1 |
mysql> show variables like '%storage_engine%'; |
你要看user表用了什么引擎(在顯示結果里參數engine后面的就表示該表當前用的存儲引擎):
1 |
mysql> show create table user; |
將user表修為InnoDB存儲引擎(也可以此命令將InnoDB換為MyISAM):
1 |
mysql> ALTER TABLE user ENGINE=INNODB; |
如果要更改整個數據庫表的存儲引擎,一般要一個表一個表的修改,比較繁瑣,可以采用先把數據庫導出,得到SQL,把MyISAM全部替換為INNODB,再導入數據庫的方式。
轉換完畢后重啟mysql
1 |
service mysqld restart |
總結
到此為止,Spring Boot整合單元測試就基本完結,關於MockMvc以及assertThat的用法大家可以繼續深入研究。后續會整合Swagger UI這個API文檔工具,即提供API文檔又提供測試接口界面,相當好用。
想要查看更多Spring Boot干貨教程,可前往:Spring Boot干貨系列總綱