SpringBoot中Mockito單元測(cè)試入門
Mock 測(cè)試就是在測(cè)試過程中,創(chuàng)建一個(gè)假的對(duì)象,避免你為了測(cè)試一個(gè)方法,卻要自行構(gòu)建整個(gè) Bean 的依賴鏈。
舉個(gè)例子:
類 A 需要調(diào)用類 B 和類 C,而類 B 和類 C 又需要調(diào)用其他類如 D、E、F 等,假設(shè)類 D 是一個(gè)外部服務(wù),那就會(huì)很難測(cè),因?yàn)槟愕姆祷亟Y(jié)果會(huì)直接的受外部服務(wù)影響,導(dǎo)致你的單元測(cè)試可能今天會(huì)過、但明天就過不了了。
而當(dāng)我們引入 Mock 測(cè)試時(shí),就可以創(chuàng)建一個(gè)假的對(duì)象,替換掉真實(shí)的 Bean B 和 C,這樣在調(diào)用B、C的方法時(shí),實(shí)際上就會(huì)去調(diào)用這個(gè)假的 Mock 對(duì)象的方法,而我們就可以自己設(shè)定這個(gè) Mock 對(duì)象的參數(shù)和期望結(jié)果,讓我們可以專注在測(cè)試當(dāng)前的類 A,而不會(huì)受到其他的外部服務(wù)影響,這樣測(cè)試效率就能提高很多。
Mockito 是一種 Java Mock 框架,主要就是用來做 Mock 測(cè)試的,它可以模擬任何 Spring 管理的 Bean、模擬方法的返回值、模擬拋出異常等等,同時(shí)也會(huì)記錄調(diào)用這些模擬方法的參數(shù)、調(diào)用順序,從而可以校驗(yàn)出這個(gè) Mock 對(duì)象是否有被正確的順序調(diào)用,以及按照期望的參數(shù)被調(diào)用。
像是 Mockito 可以在單元測(cè)試中模擬一個(gè) Service 返回的數(shù)據(jù),而不會(huì)真正去調(diào)用該 Service,通過模擬一個(gè)假的 Service 對(duì)象,來快速的測(cè)試當(dāng)前想要測(cè)試的類。
目前在 Java 中主流的 Mock 測(cè)試工具有 Mockito、JMock、EasyMock等等,而 SpringBoot 目前默認(rèn)的測(cè)試框架是 Mockito 框架。
使用 Mockitopom依賴<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope></dependency>
一個(gè) UserService,兩個(gè)方法
getUserById() -------------> 調(diào)用 UserDao 這個(gè) bean的 getUserById() insertUser() -------------> 調(diào)用 UserDao 這個(gè) bean的 insertUser()@Componentpublic class UserService {@Autowired private UserDao userDao; public User getUserById(Integer id) {return userDao.getUserById(id); } public Integer insertUser(User user) {return userDao.insertUser(user); }}
【POJO對(duì)象】
public class User { private Integer id; private String name; //省略 getter/setter}【常規(guī)操作】
如果這時(shí)候我們先不使用 Mockito 模擬一個(gè)假的 userDao Bean,而是真的去調(diào)用一個(gè)正常的 Spring Bean 的 userDao 的話, 注入 userService Bean,然后去調(diào)用方法,而他會(huì)再去調(diào)用 userDao 取得數(shù)據(jù)庫的數(shù)據(jù),然后我們?cè)賹?duì)返回結(jié)果做 Assert 斷言檢查。
@RunWith(SpringRunner.class)@SpringBootTestpublic class UserServiceTest { //先普通的注入一個(gè)userService bean @Autowired private UserService userService; @Test public void getUserById() throws Exception {//普通的使用userService,他里面會(huì)再去調(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'); }}【Mockito】
但是如果 userDao 還沒寫好,又想先測(cè) userService 的話,就需要使用 Mockito 去模擬一個(gè)假的 userDao 出來。
具體怎么搞呢?
在 userDao 上加上一個(gè) @MockBean 注解
當(dāng) userDao 被加上這個(gè)注解之后,表示 Mockito 會(huì)幫我們創(chuàng)建一個(gè)假的 Mock 對(duì)象,替換掉 Spring 中已存在的那個(gè)真實(shí)的 userDao Bean,也就是說,注入進(jìn) userService 的 userDao Bean,已經(jīng)被我們替換成假的 Mock 對(duì)象了,所以當(dāng)我們?cè)俅握{(diào)用 userService 的方法時(shí),會(huì)去調(diào)用的實(shí)際上是 mock userDao Bean 的方法,而不是真實(shí)的 userDao Bean。
當(dāng)我們創(chuàng)建了一個(gè)假的 userDao 后,我們需要為這個(gè) mock userDao 自定義方法的返回值,這里有一個(gè)公式用法,下面這段代碼的意思為,當(dāng)調(diào)用了某個(gè) Mock 對(duì)象的方法時(shí),就回傳我們想要的自定義結(jié)果。
Mockito.when( 對(duì)象.方法名() ).thenReturn( 自定義結(jié)果 )
代碼如下:
@RunWith(SpringRunner.class)@SpringBootTestpublicclass UserServiceTest {@Autowired private UserService userService;@MockBean private UserDao userDao; @Test public void getUserById() throws Exception {// 定義當(dāng)調(diào)用mock userDao的getUserById()方法,并且參數(shù)為3時(shí),就返回id為200、name為I’m mock3的user對(duì)象Mockito.when(userDao.getUserById(3)).thenReturn(new User(200, 'Aritisan'));// 返回的會(huì)是名字為I’m mock 3的user對(duì)象User user = userService.getUserById(1);Assert.assertNotNull(user);Assert.assertEquals(user.getId(), new Integer(200));Assert.assertEquals(user.getName(), 'Aritisan'); }}
Mockito 除了最基本的 Mockito.when( 對(duì)象.方法名() ).thenReturn( 自定義結(jié)果 ),還提供了其他用法讓我們使用。
thenReturn
當(dāng)使用任何整數(shù)值調(diào)用 userService 的 getUserById() 方法時(shí),就回傳一個(gè)名字為Aritisan的 User 對(duì)象。
Mockito.when(userService.getUserById(Mockito.anyInt())).thenReturn(new User(3, 'Aritisan'));User user1 = userService.getUserById(3); // 回傳的user的名字為AritisanUser user2 = userService.getUserById(200); // 回傳的user的名字也為Aritisan
限制只有當(dāng)參數(shù)的數(shù)字是 3 時(shí),才會(huì)回傳名字為 Aritisan 的 user 對(duì)象。
Mockito.when(userService.getUserById(3)).thenReturn(new User(3, 'Aritisan'));User user1 = userService.getUserById(3); // 回傳的user的名字為AritisanUser user2 = userService.getUserById(200); // 回傳的user為null
當(dāng)調(diào)用 userService 的 insertUser() 方法時(shí),不管傳進(jìn)來的 user 是什么,都回傳 100。
Mockito.when(userService.insertUser(Mockito.any(User.class))).thenReturn(100);Integer i = userService.insertUser(new User()); //會(huì)返回100
thenThrow
當(dāng)調(diào)用 userService 的 getUserById() 時(shí)的參數(shù)是 9 時(shí),拋出一個(gè) RuntimeException。
Mockito.when(userService.getUserById(9)).thenThrow(new RuntimeException('mock throw exception'));User user = userService.getUserById(9); //會(huì)拋出一個(gè)RuntimeException
如果方法沒有返回值的話(即是方法定義為 public void myMethod() {…}),要改用 doThrow() 拋出 Exception。
Mockito.doThrow(new RuntimeException('mock throw exception')).when(userService).print();userService.print(); //會(huì)拋出一個(gè)RuntimeException
verify
檢查調(diào)用 userService 的 getUserById()、且參數(shù)為3的次數(shù)是否為1次。
驗(yàn)證調(diào)用順序,驗(yàn)證 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));
Mockito 注意事項(xiàng)
上述就是 Mockito 的 Mock 對(duì)象使用方法,不過當(dāng)使用 Mockito 在 Mock 對(duì)象時(shí),有一些限制需要遵守:
不能 Mock 靜態(tài)方法 不能 Mock private 方法 不能 Mock final class因此在寫代碼時(shí),需要做良好的功能拆分,才能夠使用 Mockito 的 Mock 技術(shù),幫助我們降低測(cè)試時(shí) Bean 的耦合度。
到此這篇關(guān)于SpringBoot中Mockito單元測(cè)試入門的文章就介紹到這了,更多相關(guān)SpringBoot Mockito單元測(cè)試 內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!
相關(guān)文章:
1. IntelliJ IDEA設(shè)置默認(rèn)瀏覽器的方法2. HTTP協(xié)議常用的請(qǐng)求頭和響應(yīng)頭響應(yīng)詳解說明(學(xué)習(xí))3. idea設(shè)置提示不區(qū)分大小寫的方法4. CentOS郵件服務(wù)器搭建系列—— POP / IMAP 服務(wù)器的構(gòu)建( Dovecot )5. IntelliJ IDEA創(chuàng)建web項(xiàng)目的方法6. VMware中如何安裝Ubuntu7. docker容器調(diào)用yum報(bào)錯(cuò)的解決辦法8. .NET SkiaSharp 生成二維碼驗(yàn)證碼及指定區(qū)域截取方法實(shí)現(xiàn)9. 原生JS實(shí)現(xiàn)記憶翻牌游戲10. ASP.NET MVC通過勾選checkbox更改select的內(nèi)容
