测试用例-单元测试
單元測試——編寫手冊
1.簡述
本文主要針對如何使用Junit編寫單元測試進行描述
文中的實例基于Junit 4
| 只能以接口為維度進行測試 | 只需被測試的單元邏輯正常即可 |
| 工程必須編譯通過并打包進行部署 | 可以不依賴外部,測試進度不再受制于外部條件 |
| 工程的外部依賴(數據庫、調用的服務等)必須就緒 | 可以以方法為維度進行測試 |
| 難以測試復雜的邏輯分支,為測試數據需要調整各個數據源(數據庫、緩存、消息隊列) | 可以根據單元的邏輯復雜程度編排測試用例數量,測試使用的數據可以自由調整 |
2.創建測試用例
2.1工程準備
確保工程的maven依賴中包含junit,版本至少為4.12,一般包含在spring-boot-starter-test中;
確保工程的目錄中包含src/test/java和src/test/resource;
*部分工程沒有src/test的相關包,需要手動創建
*src/test/java主要用于存放測試用例和測試相關的類
*src/test/resoutces主要用于存放測試使用的配置和資源文件
2.2 編寫測試啟動類
許多工程在啟動時需要加載許多配置類,與外部系統進行連接,非常復雜 為了使單元測試更加輕便,我們可以編寫單元測試專用的啟動類,屏蔽一些不相關的啟動項 若你的工程啟動和外部連接依賴本身就很簡單,可以省略這一步,不寫測試啟動類則執行測試時默認使用 src/main/java下的啟動類 import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType;@ComponentScan(basePackages = { "需要掃描的包名" }, excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = { 掃描的包中需要排除的類 }) }) @SpringBootApplication(exclude = { 需要排除的一些啟動時自動配置類 }) public class TestApplication {public static void main(String[] args) {SpringApplication.run(TestApplication.class, args);} }這個啟動類中常用的配置如下
用于掃描指定的包,excludeFilters用于排除掃描的包中不需要的Bean
用于排除一起自動配置的啟動類,防止諸如數據庫、Mongo等啟動類進行外部連接
測試的啟動類與工程的啟動類類似,它通常防止在src/test/java下的項目根包中
其中“需要掃描的包名”和“掃描的包中需要排除的類”根據具體的工程決定,可按需將工程中的一些啟動時配置類排除在外(Cache配置等),保證你測試的類以及它們的直接依賴被掃描到即可
“需要排除的一些啟動時自動配置類”可參考如下對照表按需排除
| 作用 | 全類名 |
| MongoDB自動配置 | org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration |
| org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration | |
| 數據源自動配置 | org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration |
| Hibernate注解自動配置 | org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration |
啟動類的掃描配置決定了測試用例的范圍以及啟動速度
可以按需編寫多個不同掃描范圍的啟動類以適配不同的測試需求
2.3 構建抽象測試類
根據啟動類創建一個抽象測試類是個好方法,它能夠讓你在創建測試用例時通過選擇繼承的父類直接確定使用的測試啟動配置。
import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = WebEnvironment.NONE, classes = 啟動類) public abstract class BaseTest {}@SpringBootTest
用于指定測試的一些主要參數,主要是classes,用于指定關聯的啟動類,這樣繼承自這個類的所有子類都會使用相同的啟動類
2.4 使用測試專用的配置
就像在工程部署時我們可以指定不同環境的配置文件一樣;
執行測試時我們也可以使用不同的配置文件;
使用自定義測試啟動類的情況下將配置文件放置在src/test/resource下則測試類默認會使用該包下的配置和資源文件;
由于測試啟動類中通常會排除許多與測試目標無關的類,所以測試專用的配置文件可以更加輕量化,哪些被排除的類所引用的配置都可以省略;
2.5 新建Test類
針對一個被測類創建一個測試類 測試類通常以被測類的名字+Test進行命名,并放置在與src/test/java下與被測類同包名的包中編寫用例
3.1 了解Mock測試
一個Bean通常會形成一個依賴樹,這種發散的依賴結構導致我們在測試一個類的邏輯時其實是連同它所依賴的邏輯一同進行測試
此時使用Mock測試就可以解決依賴過多,邏輯復雜的問題
Mock以為模擬、虛擬,就是將原有的邏輯進行模擬,使用規劃的Mock方案邏輯進行替代
在Mock測試中,我們通常對測試對象的所有直接依賴進行Mock,被Mock的直接依賴將變成只有方法簽名的空殼
被調用時它們不會再調用間接依賴,也不會執行原有的邏輯,只會根據Mock方案進行返回
這樣在測試測試對象時我們就不在需要關心它負載的間接依賴關系和所有依賴的內部邏輯了,只需要專注于當前測試對象的邏輯即可
3.2 完全Mock依賴
這種測試僅測試測試對象內部的邏輯,屏蔽所有測試對象的依賴
適用于測試內部邏輯較為復雜的對象,或依賴較為復雜的對象
樣例:
注意
3.3 完全真實依賴
這種類型的測試同時測試測試對象及全部其依賴的邏輯,針對實際調用流程進行測試 適用于測試一個完整的流程以及測試調用鏈路正確性樣例
注意
3.4 Mock與真實依賴相結合
這種類型的測試同時測試測試對象及其部分依賴的邏輯,屏蔽部分較為復雜的依賴同時對實際調用流程進行測試 適用于測試一個部分依賴較為復雜的流程以及測試調用鏈路正確性樣例:
注意
3.5 混合測試
對一個測試對象中的不同方法采取不同的測試策略 對同一個測試對象的不同方法根據需求采取“完全Mock”或“完全真實”或“Mock與真實結合”的方式;沒有對同一個測試 對象編寫多個不同類型測試用例的需求注意
4. FAQ
4.1 @MockBean與@SpyBean
在進行mock測試或混合測試時可以看到這2中不同的設定測試對象直接依賴的注解| @MockBean | 提供mock測試能力mock方案的作用域都在一個@Test內 | 默認將標記該注解的類及其依賴的方法全部掏空,直接調用將沒有任何執行邏輯并返回null(如果有返回值) |
| @SpyBean | 提供mock測試能力mock方案的作用域都在一個@Test內 | 默認將標記該注解的類以及依賴的方法保持原樣,直接調用將按照方法原本的邏輯執行并返回;可以按照混合測試的需求靈活決定是否mock |
| @MockBean | 適用于標記的依賴需要被完全mock的場景,不配合mock方案使用易引發空指針異常 |
| @SpyBean | 適用于標記的依賴只有部分邏輯需要mock的場景,不配合mock方案則執行原方法,配合mock方案則執行mock方案 |
4.2 mock方案
在進行mock測試時需要提前針對被mock的邏輯進行規劃
參照如下寫法:
| doReturn(mockData) | 模擬方法正常返回數據 |
| doNothing() | 模擬方法未執行(可用于沒有返回值的方法) |
| doThrow(exception) | 模擬方法執行時拋出異常 |
| demoService | 被@MockBean或@SpyBean標記的測試對象的直接依賴 |
| function、arg1、arg2 | 直接依賴的方法名及其參數 |
| Matchers.eq() | org.mockito.Matchers提供的一些更靈活的mock調用時參數驗證方法;驗證通過時方法返回模擬的返回數據,驗證不通過時方法按照@MockBean或@SpyBean的默認策略執行 |
4.3 同名方法調用多次
同名方法設置多個mock方案時,不同入參預期的方案都會保留,相同入參預期的方案以最后一個為準
例,設置了如下mock方案:
方案1,3會生效,方案2會被方案3覆蓋而失效
調用demoService.callFun(String)方法時:
若入參為"alpha",則返回demoDataA;
若入參為"beta",則執行該方法的真實邏輯并返回;
若入參為其他,則執行空方法并返回null(@MockBean的默認方案);
在上述場景的基礎上,若被測方法中存在同一方法調用多次且入參相同,但根據執行順序返回值不同,則參照如下寫法(使用鏈式調用依序排布多個方案):
調用demoService.callFun(String)方法時:
若入參為"alpha",則返回demoDataA;
若入參為"beta"且是該入參的第1次調用,則返回demoDataB;
若入參為"beta"且是該入參的第2次調用,執行該方法的真實邏輯并返回;
若入參為"beta"且是該入參的第3次及以上調用,執行方案鏈中最后一個方案(例子中為調用真實邏輯);
若入參為其他,則執行空方法并返回null(@MockBean的默認方案)
4.4 驗證調用是否執行
在mock測試中需要驗證mock方案標記的方法是否按照預期的入參執行過,若缺少這一步可能導致測試用例出現未預期的成功
例,一個用例按如下步驟執行:
上述例子的用例可能會在某次數據庫數據變動,外部鏈接中斷的場景下執行失敗
增加驗證調用是否執行的步驟可以解決該問題
寫法如下:
與mock方案中的寫法非常相似
當同名方法調用多次且入參一致時采用如下寫法:
總結
- 上一篇: 计算机考研数据结构答案,计算机考研数据结
- 下一篇: 求助wpe封包遇到动态验证怎么办