單元測試中使用Mockito模擬對象

單元測試應該小巧玲瓏,輕盈快捷。然而,一個待測的對象可能依賴另一個對象。它可能需要跟數據庫、郵箱服務器、Web Service、消息隊列等服務進行交互。但是,這些服務可能在測試過程中不可用。假設這些服務可用,依賴這些服務的單元測試可能相當耗時。要是

  1. Web Service 不可獲得。
  2. 數據庫因維護而關閉。
  3. 消息隊列笨重且緩慢。

這些違背單元測試小巧玲瓏,輕盈快捷的初衷。單元測試被期待在幾毫秒內執行完成。若單元測試緩慢,你的開發過程受阻,這會影響你開發組的效率。解決之道就是模擬(Mocking),

若你遵循OOP的SOILD原則,且使用Spring的依賴注入,單元測試中的模擬Mock變得輕而易舉。你不必連接數據庫。你只需一個能返回你期待值的對象。若你編寫緊密耦合代碼,模擬會相當艱難。我目睹過許多因緊密耦合其它對象的遺留代碼不能單元測試。不可測試代碼不遵循OOP的SOILD原則,且不能使用依賴注入。

Mockito初體驗

接下來將學習使用Mockito框架。它是一套通過簡單的方法對于指定接口或類生產Mock對象的類庫。使用Mockito,在準備階段只需少量時間,可以使用簡潔的API編寫漂亮的測試,可以對具體類創建Mock對象,并且有監視非Mock對象的功能。

這有兩個術語需要了解一下。

  • Stub對象作用是在測試時提供所需的測試數據,可以對各種交互設置相應的回應。Mockito中when(...).thenReturn(...)這樣的語法便是設置方法調用的返回值。另外也可以設置方法在何時調用會拋異常等。

  • Mock對象用來驗證測試中所依賴對象間的交互是否能夠達到預期。Mockito中用verify(...).methodXxx(...)語法驗證methodXxx()方法是否按照預期進行調用。

需要加入到pom.xml的依賴如下:

<dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>2.16.0</version><scope>test</scope>
</dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope>
</dependency>

創建Mock對象

可以通過兩種方法來Mock對象

  1. 通過mock(Class<T> clazz)方法。
  2. 通過@Mock注解需要Mock的對象,然后調用MockitoAnnotations.initMocks(this)@RunWith(MockitoJUnitRunner.class)初始化模擬。
//@RunWith(MockitoJUnitRunner.class)
public class MockitoSampleTest {// 模擬接口UserService mockUserService = mock(UserService.class);// 模擬實現類UserServiceImpl mockServiceImpl = mock(UserServiceImpl.class);// 基于注釋模擬類@MockUser mockUser;@Beforepublic void initMocks() {// 初始化當前測試類所有@Mock注釋模擬對象MockitoAnnotations.initMocks(this);}
}

值得注意的是,對于final類、匿名類和Java的基本類型是無法進行Mock的。

設定Mock對象的期望值行為及返回值

有兩種通用基礎設定寫法:

  1. when(...).thenReturn(...);
  2. doReturn(...).when(...).someMethod();

但是,doReturn(...).when(mockObj.someMethod());會拋異常。

@Test
public void testMockClass() {// 對方法設定返回值,也就是設置數據樁when(mockServiceImpl.findUserByUserName("tom")).thenReturn(new User("tom", "1234"));doReturn(true).when(mockServiceImpl).hasMatchUser("tom", "1234");User user = mockServiceImpl.findUserByUserName("tom");boolean isMatch = mockServiceImpl.hasMatchUser("tom", "1234");assertNotNull(user);assertEquals(user.getUserName(), "tom");assertEquals(isMatch, true);}

也值得注意的是,static和final修飾的方法無法進行設定的。

驗證交互行為

Mock對象一旦建立便會自動記錄自己的交互行為,所以,可以有選擇地對其交互行為進行驗證。

@Test
// 模擬接口UserService測試
public void testMockInterface() {// 對方法設定返回值,也就是設置數據樁when(mockUserService.findUserByUserName("tom")).thenReturn(new User("tom", "1234"));doReturn(true).when(mockUserService).hasMatchUser("tom", "1234");// 對void方法進行方法預期設定User u = new User("John", "1234");doNothing().when(mockUserService).registerUser(u);// 執行方法調用User user = mockUserService.findUserByUserName("tom");boolean isMatch = mockUserService.hasMatchUser("tom", "1234");mockUserService.registerUser(u);assertNotNull(user);assertEquals(user.getUserName(), "tom");assertEquals(isMatch, true);// 驗證交互行為verify(mockUserService).findUserByUserName("tom");// 驗證方法只調用一次verify(mockUserService, times(1)).findUserByUserName("tom");// 驗證方法至少調用一次verify(mockUserService, atLeastOnce()).findUserByUserName("tom");verify(mockUserService, atLeast(1)).findUserByUserName("tom");// 驗證方法至多調用一次verify(mockUserService, atMost(1)).findUserByUserName("tom");verify(mockUserService).hasMatchUser("tom", "1234");verify(mockUserService).registerUser(u);
}

對Service層進行單元測試

同常主要Java Web應用分Controller,Service,DAO基本三層來進行開發。

接下來通過使用Mockito框架對Service進單元測試。

Domain領域對象:

public class Product {}

DAO數據連接層:

public interface ProductDao {int getAvailableProducts(Product product);int orderProduct(Product product, int orderedQuantity);
}

Service業務邏輯層:

public class ProductService {private ProductDao productDao;public boolean buy(Product product, int orderedQuantity) {int availableQuantity = productDao.getAvailableProducts(product);if (orderedQuantity > availableQuantity) {return false;}productDao.orderProduct(product, orderedQuantity);return true;}}

Service測試用例:

public class ProductServiceTest {private ProductDao productDao;@Beforepublic void setupMock() {//模擬Dao層productDao = mock(ProductDao.class);}@Testpublic void testBuy() {int availableQuantity = 30;Product product = new Product();ProductService productService = new ProductService();//設置數據樁when(productDao.getAvailableProducts(product)).thenReturn(availableQuantity);//doReturn(availableQuantity).when(productDao).getAvailableProducts(product);//這寫法不行//doReturn(availableQuantity).when(productDao.getAvailableProducts(product));//通過Spring測試框架提供的工具類為目標對象私有屬性設值,這樣就不用ProductDao另建setProductDao()方法ReflectionTestUtils.setField(productService, "productDao", productDao);Assert.assertFalse(productService.buy(product, 31));Assert.assertTrue(productService.buy(product, 3));//驗證交互行為verify(productDao).orderProduct(product, 3);verify(productDao, times(2)).getAvailableProducts(product);}}

測試用例中,用到Spring test框架的ReflectionTestUtils,它可以為目標對象非公有屬性設值,或調用非公有setter方法,方便測試過程中使用。

為了使用ReflectionTestUtils,需要向pom.xml添加下面的依賴

<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>3.0.5.RELEASE</version><scope>test</scope>
</dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>3.0.5.RELEASE</version><scope>test</scope>
</dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version><scope>test</scope>
</dependency>

總結

本文介紹了Mockito的基本用法,以及通過它Mock對象對Service層輔助測試用例。在Mockito輔助下,單元測試變得如虎添翼啊!

在編寫代碼過程中,必須反復調試它,保證他順利通過。雖代碼通過編譯,只是說明其語法正確,但不能保證其語義也正確。沒有任何人可以輕易承諾這段代碼的行為一定是正確的。幸運的是,單元測試會為我們的承諾作出保證。編寫單元測試就是用來驗證這段代碼的行為是否與我們期望的一樣。有了單元測試,我們可以自信地交付自己的代碼,減少后顧之憂。

引用

  1. Mocking in Unit Tests with Mockito

  2. Mockito (Mockito 2.16.0 API)

  3. 《Spring 3.x企業應用開發實戰》陳雄華、林開雄 著

  4. Spring Framework Reference Documentation 11. Testing

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/445705.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/445705.shtml
英文地址,請注明出處:http://en.pswp.cn/news/445705.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

足球點球 html5,身為西甲第一點球手,C羅只有5次讓點經歷,難怪點球破門過百...

當今足壇是C羅和梅西的天下&#xff0c;兩人持續不斷的刷新創造各種記錄&#xff0c;讓球迷應接不暇。比梅西大2歲的C羅近期更是開掛&#xff0c;今天說他的點球。C羅現在是西甲歷史上的第一點球手本賽季西甲第21輪&#xff0c;皇馬客場4-1大勝瓦倫西亞&#xff0c;C羅在比賽中…

Spring Cloud 學習筆記(2 / 3)

Spring Cloud 學習筆記&#xff08;1 / 3&#xff09; Spring Cloud 學習筆記&#xff08;3 / 3&#xff09; ---56_Hystrix之全局服務降級DefaultProperties57_Hystrix之通配服務降級FeignFallback58_Hystrix之服務熔斷理論59_Hystrix之服務熔斷案例(上)60_Hystrix之服務熔斷…

html5的colgroup,HTML colgroup 標簽 | 菜鳥教程

HTML 標簽實例 和 標簽為表格中的三個列設置了背景色&#xff1a;ISBNTitlePrice3476896My first HTML$53嘗試一下 瀏覽器支持所有主流瀏覽器都支持 標簽。標簽定義及使用說明 標簽用于對表格中的列進行組合&#xff0c;以便對其進行格式化。通過使用 標簽&#xff0c;可以向…

Spring Cloud 學習筆記(3 / 3)

Spring Cloud 學習筆記&#xff08;1 / 3&#xff09; Spring Cloud 學習筆記&#xff08;2 / 3&#xff09; ---108_Nacos之Linux版本安裝109_Nacos集群配置(上)110_Nacos集群配置(下)111_Sentinel是什么112_Sentinel下載安裝運行113_Sentinel初始化監控114_Sentinel流控規則…

普林斯頓計算機科學系,普林斯頓大學計算機科學系

普林斯頓大學計算機科學系研究生階段開設有以下學位項目&#xff0c;分別是計算機科學博士&#xff1a;為期5年&#xff0c;要求申請者本科畢業&#xff0c;不限專業背景&#xff0c;但通常被錄取的學生擁有工程、理科或數學專業背景&#xff0c;未設定本科GPA要求&#xff0c;…

html5語句大全,html5基礎語句(學習)

起風了兼容性問題文檔類型設定字符設定常用新標簽新增的input type屬性值&#xff1a;常用新屬性綜合案例兼容性問題文檔類型設定documentHTMLhttp://blog.sina.com.cn/s/blog_6b38618701011csx.html(html5-api)http://blog.csdn.net/temotemo/article以多維模型為核心&#xf…

html單選按鈕for,HTML如何實現RadioButton單選按鈕

用HTML來實現RadioButton&#xff0c;需要使用input標簽&#xff0c;其中type指定為radio&#xff0c;接下來的文章我們就來說一說詳細的內容。我們先來看input標簽的格式注&#xff1a;對于需要選中檢索的值&#xff0c;可以利用表單的提交或使用JavaScript獲取。我們來看具體…

計算機基礎知識整理 手抄報,科技小制作的手抄報資料簡單字少

科學是一切生活的基礎&#xff0c;如同一把鑰匙&#xff0c;為我們開辟前進的道路。科技在我們的生活中也是很重要的。你會做科技手抄報嗎?下面是學習啦小編為大家帶來的有關科技小制作的手抄報&#xff0c;希望大家喜歡。有關科技小制作的手抄報的圖片參考有關科技小制作的手…

《深入理解JVM.2nd》筆記(五):調優案例分析與實戰

文章目錄概念案例分析高性能硬件上的程序部署策略情景再現1問題分析1關于Full GC使用64位JDK來管理大內存可能遇到問題建立邏輯集群使用邏輯集群可能遇到的問題最后解決方案集群間同步導致的內存溢出情景再現2問題分析2堆外內存導致的溢出錯誤情景再現3問題分析3外部命令導致系…

計算機課是一體化教學嗎,計算機基礎課程理實一體化教學模式

計算機基礎課程理實一體化教學模式摘要:“大學計算機基礎”是高職人材培養各專業計算機技術應用的基礎。本文圍繞這一問題,就如何培養學生更好地利用計算機去解決專業領域及日常事務中的問題,如何提高學生利用信息技術解決專業領域問題的能力和意識進行探討。對教學中推進理實一…

Java開發常見面試題詳解(并發,JVM)

預覽 并發 問題詳解請談談你對volatile的理解linkCAS你知道嗎&#xff1f;link原子類Atomiclnteger的ABA問題談談?原子更新引用知道嗎&#xff1f;link我們知道ArrayList是線程不安全&#xff0c;請編碼寫一個不安全的案例并給出解決方案link公平鎖/非公平鎖/可重入鎖/遞歸鎖…

計算機組網配置,計算機組網技術與配置教案..doc

計算機組網技術與配置教案.第一節課課程名稱第1章 計算機組網技術基礎學時2課程內容1.1、計算機網絡介紹&#xff0c;局域網概念&#xff0c;雙絞線和網絡傳輸介質&#xff0c;組網軟硬件具體介紹教學目的掌握計算機網絡的一般概念掌握局域網的定義了解網絡軟硬件組成了解傳輸介…

怎樣恢復計算機管理員身份,電腦高手必備:2招教你找回Windows管理員權限?

原標題&#xff1a;電腦高手必備&#xff1a;2招教你找回Windows管理員權限&#xff1f;昨晚有個小伙伴提問有關Windows賬戶信息的問題&#xff0c;雖然描述上有些籠統吧&#xff0c;但根據小編多年的經驗猜測(此處是得意臉)&#xff0c;不是某些安全軟件把當前賬戶的管理員模式…

K12計算機科學標準,計算機科學新主張(K12基礎教育) 翻譯.pptx

《計算機科學新主張(K12基礎教育) 翻譯.pptx》由會員分享&#xff0c;可在線閱讀&#xff0c;更多相關《計算機科學新主張(K12基礎教育) 翻譯.pptx(26頁珍藏版)》請在人人文庫網上搜索。1、計算機與軟件科學正在改變一切,but the majority of schools dont teach computer scie…

樓主考南師計算機學碩,【圖片】2019南師大新傳學碩考研經驗貼【南京師范大學研究生吧】_百度貼吧...

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓第二階段&#xff1a;7月—8月我個人認為這一階段是在整個專業課復習過程中相對來說最重要的。因為暑假不用上課不用寫作業&#xff0c;基本沒有任何干擾&#xff0c;可以全心全意的投入復習當中。復習的地點大家隨意&#xff0c;我…

計算機中丟失mpr.dll,mprmsg.dll

mprmsg.dll是一款電腦系統缺失修復文件&#xff0c;該文件是很多程序運行的基礎&#xff0c;丟失后會報錯&#xff0c;同時某些時候會發生文件誤刪導致丟失&#xff0c;這時只要下載該文件進行修復即可。軟件介紹mprmsg.dll官方版是一款小巧實用的電腦操作系統專用應用程序擴展…

計算機分類及在信息社會中的應用,計算機在信息社會中的應用.doc

計算機在信息社會中的應用PAGE 11武漢市事業單位工人技能鑒定 技師論文課題名稱 計算機在信息社會中的應用 學生姓名 工 種 工作單位 指導老師 完 成 時 間&#xff1a; 2013年 月 日目 錄TOC \o "1-3" \h \u HYPERLINK \l _Toc18761 一、 前言 PAGEREF _Toc18761 2 …

ajax獲取qq音樂源碼,ajax請求QQ音樂

搜索歌曲function go() {var val document.getElementById("name").value;let url ‘https://c.y.qq.com/soso/fcgi-bin/search_for_qq_cp?g_tk5381&uin0&formatjsonp&inCharsetutf-8&outCharsetutf-8&notice0&platformh5&needNewCod…

Java開發常見面試題詳解(LockSupport,AQS,Spring循環依賴,Redis)

總覽 問題詳解String.intern()的作用linkLeetCode的Two Sum題link什么是可重入鎖&#xff1f;link談談LockSupportlink談談AQSlinkSpring的AOP順序linkSpring的循環依賴linkRedis各基本類型應用場景linkRedis分布式鎖linkRedis內存配置及內存淘汰策略link實現LRU算法link ---…

構造與析構(C++)

2.2 構造與析構2.2.1 構造函數2.2.2 缺省構造函數2.2.3 構造函數的重載2.2.4 類型轉換構造函數2.2.5 拷貝構造函數 2.2 構造與析構 2.2.1 構造函數 構造函數(constructor)是與類同名的特殊成員函數&#xff0c;主要用來初始化對象的數據成員。 像上一個例子&#xff0c;初始…