SpringBoot單元測試框架Mockito介紹及使用
Mockito 是一種 Java mock 框架,他主要是用來做 mock 測試的,他可以模擬任何 Spring 管理的 bean、模擬方法的返回值、模擬拋出異常...等,在了解 Mockito 的具體用法之前,得先了解什麼是 mock 測試
1. 什么是mock測試
mock 測試就是在測試過程中,創(chuàng)建一個假的對象,避免你為了測試一個方法,卻要自行構(gòu)建整個 bean 的依賴鏈
像是以下這張圖,類 A 需要調(diào)用類 B 和類 C,而類 B 和類 C 又需要調(diào)用其他類如 D、E、F 等,假設(shè)類 D 是一個外部服務(wù),那就會很難測,因為你的返回結(jié)果會直接的受外部服務(wù)影響,導(dǎo)致你的單元測試可能今天會過、但明天就過不了了
而當(dāng)我們引入 mock 測試時,就可以創(chuàng)建一個假的對象,替換掉真實的 bean B 和 C,這樣在調(diào)用B、C的方法時,實際上就會去調(diào)用這個假的 mock 對象的方法,而我們就可以自己設(shè)定這個 mock 對象的參數(shù)和期望結(jié)果,讓我們可以專注在測試當(dāng)前的類 A,而不會受到其他的外部服務(wù)影響,這樣測試效率就能提高很多
2. Mockito簡介
說完了 mock 測試的概念,接下來我們進(jìn)入到今天的主題,Mockito
Mockito 是一種 Java mock 框架,他主要就是用來做 mock 測試的,他可以模擬任何 Spring 管理的 bean、模擬方法的返回值、模擬拋出異常...等,他同時也會記錄調(diào)用這些模擬方法的參數(shù)、調(diào)用順序,從而可以校驗出這個 mock 對象是否有被正確的順序調(diào)用,以及按照期望的參數(shù)被調(diào)用
像是 Mockito 可以在單元測試中模擬一個 service 返回的數(shù)據(jù),而不會真正去調(diào)用該 service,這就是上面提到的 mock 測試精神,也就是通過模擬一個假的 service 對象,來快速的測試當(dāng)前我想要測試的類
目前在 Java 中主流的 mock 測試工具有 Mockito、JMock、EasyMock..等,而 SpringBoot 目前內(nèi)建的是 Mockito 框架
題外話說一下,Mockito 是命名自一種調(diào)酒莫吉托(Mojito),外國人也愛玩諧音梗。。。
3. 在SpringBoot單元測試中使用Mockito
首先在 pom.xml 下新增 spring-boot-starter-test 依賴,該依賴內(nèi)就有包含了 JUnit、Mockito
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
先寫好一個 UserService,他里面有兩個方法getUserById()
和insertUser()
,而他們會分別去再去調(diào)用 UserDao 這個 bean的getUserById()
和insertUser()
方法
@Component publicclass UserService { @Autowired private UserDao userDao; public User getUserById(Integer id) { return userDao.getUserById(id); } public Integer insertUser(User user) { return userDao.insertUser(user); } }
User model 的定義如下
publicclass User { private Integer id; private String name; //省略 getter/setter }
如果這時候我們先不使用 Mockito 模擬一個假的 userDao bean,而是真的去調(diào)用一個正常的 Spring bean 的 userDao 的話,測試類寫法如下。其實就是很普通的注入 userService bean,然后去調(diào)用他的方法,而他會再去調(diào)用 userDao 取得數(shù)據(jù)庫的數(shù)據(jù),然后我們再對返回結(jié)果做 assert 斷言檢查
@RunWith(SpringRunner.class) @SpringBootTest publicclass UserServiceTest { //先普通的注入一個userService bean @Autowired private UserService userService; @Test public void getUserById() throws Exception { //普通的使用userService,他里面會再去調(diào)用userDao取得數(shù)據(jù)庫的數(shù)據(jù) User user = userService.getUserById(1); //檢查結(jié)果 Assert.assertNotNull(user); Assert.assertEquals(user.getId(), new Integer(1)); Assert.assertEquals(user.getName(), "John"); } }
但是如果 userDao 還沒寫好,又想先測 userService 的話,就需要使用 Mockito 去模擬一個假的 userDao 出來
使用方法是在 userDao 上加上一個@MockBean
注解,當(dāng) userDao 被加上這個注解之后,表示 Mockito 會幫我們創(chuàng)建一個假的 mock 對象,替換掉 Spring 中已存在的那個真實的 userDao bean,也就是說,注入進(jìn) userService 的 userDao bean,已經(jīng)被我們替換成假的 mock 對象了,所以當(dāng)我們再次調(diào)用 userService 的方法時,會去調(diào)用的實際上是 mock userDao bean 的方法,而不是真實的 userDao bean
當(dāng)我們創(chuàng)建了一個假的 userDao 后,我們需要為這個 mock userDao 自定義方法的返回值,這里有一個公式用法,下面這段代碼的意思為,當(dāng)調(diào)用了某個 mock 對象的方法時,就回傳我們想要的自定義結(jié)果
Mockito.when( 對象.方法名() ).thenReturn( 自定義結(jié)果 )
使用 Mockito 模擬 bean 的單元測試具體實例如下
@RunWith(SpringRunner.class) @SpringBootTest publicclass UserServiceTest { @Autowired private UserService userService; @MockBean private UserDao userDao; @Test public void getUserById() throws Exception { // 定義當(dāng)調(diào)用mock userDao的getUserById()方法,并且參數(shù)為3時,就返回id為200、name為I'm mock3的user對象 Mockito.when(userDao.getUserById(3)).thenReturn(new User(200, "I'm mock 3")); // 返回的會是名字為I'm mock 3的user對象 User user = userService.getUserById(1); Assert.assertNotNull(user); Assert.assertEquals(user.getId(), new Integer(200)); Assert.assertEquals(user.getName(), "I'm mock 3"); } }
Mockito 除了最基本的Mockito.when( 對象.方法名() ).thenReturn( 自定義結(jié)果 )
,還提供了其他用法讓我們使用
thenReturn 系列方法
當(dāng)使用任何整數(shù)值調(diào)用 userService 的 getUserById() 方法時,就回傳一個名字為 I'm mock3 的 user 對象
Mockito.when(userService.getUserById(Mockito.anyInt())).thenReturn(new User(3, "I'm mock")); User user1 = userService.getUserById(3); // 回傳的user的名字為I'm mock User user2 = userService.getUserById(200); // 回傳的user的名字也為I'm mock
限制只有當(dāng)參數(shù)的數(shù)字是 3 時,才會回傳名字為 I'm mock 3 的 user 對象
Mockito.when(userService.getUserById(3)).thenReturn(new User(3, "I'm mock")); User user1 = userService.getUserById(3); // 回傳的user的名字為I'm mock User user2 = userService.getUserById(200); // 回傳的user為null
當(dāng)調(diào)用 userService 的 insertUser() 方法時,不管傳進(jìn)來的 user 是什麼,都回傳 100
Mockito.when(userService.insertUser(Mockito.any(User.class))).thenReturn(100); Integer i = userService.insertUser(new User()); //會返回100
thenThrow 系列方法
當(dāng)調(diào)用 userService 的 getUserById() 時的參數(shù)是 9 時,拋出一個 RuntimeException
Mockito.when(userService.getUserById(9)).thenThrow(new RuntimeException("mock throw exception")); User user = userService.getUserById(9); //會拋出一個RuntimeException
如果方法沒有返回值的話(即是方法定義為public void myMethod() {...}
),要改用 doThrow() 拋出 Exception
Mockito.doThrow(new RuntimeException("mock throw exception")).when(userService).print(); userService.print(); //會拋出一個RuntimeException
verify 系列方法
檢查調(diào)用 userService 的 getUserById()、且參數(shù)為3的次數(shù)是否為1次
Mockito.verify(userService, Mockito.times(1)).getUserById(Mockito.eq(3)) ;
驗證調(diào)用順序,驗證 userService 是否先調(diào)用 getUserById() 兩次,并且第一次的參數(shù)是 3、第二次的參數(shù)是 5,然后才調(diào)用insertUser() 方法
InOrder inOrder = Mockito.inOrder(userService); inOrder.verify(userService).getUserById(3); inOrder.verify(userService).getUserById(5); inOrder.verify(userService).insertUser(Mockito.any(User.class));
4. Mockito的限制
上述就是 Mockito 的 mock 對象使用方法,不過當(dāng)使用 Mockito 在 mock 對象時,有一些限制需要遵守
- 不能 mock 靜態(tài)方法
- 不能 mock private 方法
- 不能 mock final class
因此在寫代碼時,需要做良好的功能拆分,才能夠使用 Mockito 的 mock 技術(shù),幫助我們降低測試時 bean 的耦合度
5. 總結(jié)
Mockito 是一個非常強大的框架,可以在執(zhí)行單元測試時幫助我們模擬一個 bean,提高單元測試的穩(wěn)定性
并且大家可以嘗試在寫代碼時,從 mock 測試的角度來寫,更能夠?qū)懗龉δ芮蟹至己玫拇a架構(gòu),像是如果有把專門和外部服務(wù)溝通的代碼抽出來成一個 bean,在進(jìn)行單元測試時,只要透過 Mockito 更換掉那個 bean 就行了
到此這篇關(guān)于SpringBoot單元測試框架Mockito介紹及使用的文章就介紹到這了,更多相關(guān)SpringBoot Mockito內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳細(xì)解讀AbstractStringBuilder類源碼
這篇文章主要介紹了詳細(xì)解讀AbstractStringBuilder類源碼,具有一定參考價值,需要的朋友可以了解下。2017-12-12Java 回調(diào)機(jī)制(CallBack) 詳解及實例代碼
這篇文章主要介紹了 Java 回調(diào)機(jī)制(CallBack) 詳解及實例代碼的相關(guān)資料,需要的朋友可以參考下2017-02-02java后臺判斷客戶端是手機(jī)/PC并返回不同頁面的實例
下面小編就為大家分享一篇java后臺判斷客戶端是手機(jī)/PC并返回不同頁面的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-01-01SpringBoot 注解事務(wù)聲明式事務(wù)的方式
springboot使用上述注解的幾種方式開啟事物,可以達(dá)到和xml中聲明的同樣效果,但是卻告別了xml,使你的代碼遠(yuǎn)離配置文件。今天就扒一扒springboot中事務(wù)使用注解的玩法,感興趣的朋友一起看看吧2017-09-09java中的日期時間類Date和SimpleDateFormat
這篇文章主要介紹了java中的日期時間類Date和SimpleDateFormat,Date類的對象在Java中代表的是當(dāng)前所在系統(tǒng)的此刻日期時間,說白了就是你計算機(jī)上現(xiàn)實的時間,需要的朋友可以參考下2023-09-09