嘲弄和存根–了解Mockito的測試雙打

介紹

我遇到的一件事是使用模擬框架的團隊假設他們在模擬。
他們并不知道Mocks只是Gerard Meszaros在xunitpatterns.com上歸類的“測試雙打”之一。

重要的是要意識到每種類型的測試雙精度在測試中都扮演著不同的角色。 用與您需要學習不同模式或重構的方式相同,您需要了解每種類型的測試double的原始角色。 然后可以將它們組合起來以滿足您的測試需求。
我將簡要介紹這種分類的產生方式以及每種類型的不同之處。
我將在Mockito中使用一些簡短的簡單示例進行此操作。

非常簡短的歷史

多年來,人們一直在編寫系統組件的輕量級版本以幫助進行測試。 通常將其稱為存根。 在2000年的文章“ Endo-Testing:使用模擬對象進行單元測試”中介紹了模擬對象的概念。 從那時起,Meszaros將存根,模擬和其他許多類型的測試對象歸類為“測試雙打”。
該術語已由Martin Fowler在“ Mocks Are n't Stubs”中引用,并在Microsoft社區中被采用,如“ Exploring The Test Doubles Continuum of Test Doubles”中所示。
參考部分中顯示了指向這些重要論文的鏈接。

考試雙打的類別

上圖顯示了常用的雙重測試類型。 以下URL提供了對每個模式及其功能以及替代術語的很好的交叉引用。
http://xunitpatterns.com/Test%20Double.html

莫基托

Mockito是一個測試間諜框架,學習起來非常簡單。 Mockito值得注意的是,在測試之前沒有定義任何模擬對象的期望,因為它們有時在其他模擬框架中也是如此。 開始嘲笑時,這會導致更自然的樣式(IMHO)。
以下示例在此處純粹是為了簡單演示如何使用Mockito實施不同類型的測試雙打。

網站上有大量有關如何使用Mockito的特定示例。
http://docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html

使用Mockito測試雙打

以下是一些使用Mockito的基本示例,以顯示Meszaros定義的每個測試雙打的作用。
我為每個對象都提供了指向主要定義的鏈接,因此您可以獲得更多示例和完整定義。

虛擬對象

http://xunitpatterns.com/Dummy%20Object.html
這是所有測試雙打中最簡單的一次。 這是一個沒有實現的對象,僅用于填充與測試無關的方法調用的參數。

例如,下面的代碼使用很多代碼來創建客戶,這對測試并不重要。
只要客戶數恢復為1,該測試就不會在乎添加哪個客戶。

public Customer createDummyCustomer() {County county = new County('Essex');City city = new City('Romford', county);Address address = new Address('1234 Bank Street', city);Customer customer = new Customer('john', 'dobie', address);return customer;
}@Test
public void addCustomerTest() {Customer dummy = createDummyCustomer();AddressBook addressBook = new AddressBook();addressBook.addCustomer(dummy);assertEquals(1, addressBook.getNumberOfCustomers());
}

我們實際上并不關心客戶對象的內容,但是它是必需的。 我們可以嘗試使用null值,但是如果代碼正確,則可能會引發某種異常。

@Test(expected=Exception.class)
public void addNullCustomerTest() {Customer dummy = null;AddressBook addressBook = new AddressBook();addressBook.addCustomer(dummy);
}

為了避免這種情況,我們可以使用一個簡單的Mockito假人來獲得所需的行為。

@Test
public void addCustomerWithDummyTest() {Customer dummy = mock(Customer.class);AddressBook addressBook = new AddressBook();addressBook.addCustomer(dummy);Assert.assertEquals(1, addressBook.getNumberOfCustomers());
}

正是這個簡單的代碼創建了一個要傳遞給調用的虛擬對象。

Customer dummy = mock(Customer.class);

不要被模擬語法所迷惑-這里扮演的角色是虛擬的,而不是模擬的。
區別在于測試雙重性的作用,而不是用于創建雙重性的語法。

該類可以輕松替代客戶類,并使測試非常容易閱讀。

測試存根

http://xunitpatterns.com/Test%20Stub.html
測試存根的作用是將受控值返回到要測試的對象。 這些被描述為測試的間接輸入。 希望有一個例子可以闡明這意味著什么。

采取以下代碼

public class SimplePricingService implements PricingService
{ PricingRepository repository;public SimplePricingService(PricingRepository pricingRepository) {this.repository = pricingRepository;}@Overridepublic Price priceTrade(Trade trade) {return repository.getPriceForTrade(trade);}@Overridepublic Price getTotalPriceForTrades(Collectiontrades) {Price totalPrice = new Price();for (Trade trade : trades){Price tradePrice = repository.getPriceForTrade(trade);totalPrice = totalPrice.add(tradePrice);}return totalPrice;}

SimplePricingService具有一個協作對象,即交易存儲庫。 交易存儲庫通過getPriceForTrade方法將交易價格提供給定價服務。
為了測試SimplePricingService中的業務邏輯,我們需要控制這些間接輸入
即我們從未通過測試的輸入。 如下所示。

在以下示例中,我們對PricingRepository存根以返回可用于測試SimpleTradeService的業務邏輯的已知值。

@Test
public void testGetHighestPricedTrade() throws Exception {Price price1 = new Price(10); Price price2 = new Price(15);Price price3 = new Price(25);PricingRepository pricingRepository = mock(PricingRepository.class);when(pricingRepository.getPriceForTrade(any(Trade.class))).thenReturn(price1, price2, price3);PricingService service = new SimplePricingService(pricingRepository);Price highestPrice = service.getHighestPricedTrade(getTrades());assertEquals(price3.getAmount(), highestPrice.getAmount());
}

破壞者的例子

測試存根有兩種常見的變體:響應者和破壞者。
如前面的示例,使用響應者來測試幸福的道路。
破壞者用于測試以下異常行為。

@Test(expected=TradeNotFoundException.class)
public void testInvalidTrade() throws Exception {Trade trade = new FixtureHelper().getTrade();TradeRepository tradeRepository = mock(TradeRepository.class);when(tradeRepository.getTradeById(anyLong())).thenThrow(new TradeNotFoundException());TradingService tradingService = new SimpleTradingService(tradeRepository);tradingService.getTradeById(trade.getId());
}

模擬對象

http://xunitpatterns.com/Mock%20Object.html
模擬對象用于在測試期間驗證對象行為。 通過對象行為,我的意思是我們檢查在運行測試時是否在對象上執行了正確的方法和路徑。 這與存根的支持作用完全不同,存根用于為您要測試的任何內容提供結果。 在存根中,我們使用為方法定義返回值的模式。

when(customer.getSurname()).thenReturn(surname);

在模擬中,我們使用以下形式檢查對象的行為。

verify(listMock).add(s);

這是一個簡單的示例,我們要測試新交易是否已正確審核。
這是主要代碼。

public class SimpleTradingService implements TradingService{TradeRepository tradeRepository;AuditService auditService;public SimpleTradingService(TradeRepository tradeRepository, AuditService auditService){this.tradeRepository = tradeRepository;this.auditService = auditService;}public Long createTrade(Trade trade) throws CreateTradeException {Long id = tradeRepository.createTrade(trade);auditService.logNewTrade(trade);return id;
}

以下測試為交易存儲庫創建存根,并為AuditService創建模擬
然后,我們在模擬的AuditService上調用verify,以確保TradeService調用了
logNewTrade方法正確

@Mock
TradeRepository tradeRepository;@Mock
AuditService auditService;@Test
public void testAuditLogEntryMadeForNewTrade() throws Exception { Trade trade = new Trade('Ref 1', 'Description 1');when(tradeRepository.createTrade(trade)).thenReturn(anyLong()); TradingService tradingService = new SimpleTradingService(tradeRepository, auditService);tradingService.createTrade(trade);verify(auditService).logNewTrade(trade);
}

以下行對模擬的AuditService進行檢查。

verify(auditService).logNewTrade(trade);

該測試使我們能夠證明審計服務在創建交易時的行為正確。

測試間諜

http://xunitpatterns.com/Test%20Spy.html
值得一看上面的鏈接,以嚴格定義測試間諜。
但是,在Mockito中,我喜歡使用它來包裝實際對象,然后驗證或修改其行為以支持您的測試。 這是一個示例,我們檢查了列表的標準行為。 注意,我們既可以驗證是否調用了add方法,也可以斷言該項目已添加到列表中。

@Spy
List listSpy = new ArrayList();@Test
public void testSpyReturnsRealValues() throws Exception {String s = 'dobie';listSpy.add(new String(s));verify(listSpy).add(s);assertEquals(1, listSpy.size());
}

將其與僅可驗證方法調用的模擬對象進行比較。 因為我們僅模擬列表的行為,所以它不記錄已添加項目,并且在調用size()方法時返回默認值零。

@Mock
ListlistMock = new ArrayList();@Test
public void testMockReturnsZero() throws Exception {String s = 'dobie';listMock.add(new String(s));verify(listMock).add(s);assertEquals(0, listMock.size());
}

testSpy的另一個有用功能是能夠對返回調用進行存根。 完成此操作后,該對象將正常運行,直到調用存根方法為止。
在此示例中,我們將get方法存根以始終引發RuntimeException。 其余行為保持不變。

@Test(expected=RuntimeException.class)
public void testSpyReturnsStubbedValues() throws Exception {listSpy.add(new String('dobie'));  assertEquals(1, listSpy.size());when(listSpy.get(anyInt())).thenThrow(new RuntimeException());listSpy.get(0);
}

在此示例中,我們再次保留了核心行為,但更改了size()方法,使其最初返回1,對所有后續調用返回5。

public void testSpyReturnsStubbedValues2() throws Exception {int size = 5;when(listSpy.size()).thenReturn(1, size);int mockedListSize = listSpy.size();assertEquals(1, mockedListSize);mockedListSize = listSpy.size();assertEquals(5, mockedListSize);  mockedListSize = listSpy.size();assertEquals(5, mockedListSize);  
}

這真是不可思議!

假物件

http://xunitpatterns.com/Fake%20Object.html
假物品通常是手工制作或重量較輕的物品,僅用于測試,不適合生產。 一個很好的例子是內存數據庫或偽造的服務層。

它們往往提供比標準測試倍增功能更多的功能,因此通常不適合使用Mockito進行實現。 這并不是說它們不能像這樣構造,僅僅是因為它可能不值得采用這種方式來實現。

參考:“ 小事半解 ” – 敏捷工程技術博客上來自我們JCG合作伙伴 John Dobie的Mockito了解測試雙打 。


翻譯自: https://www.javacodegeeks.com/2012/05/mocks-and-stubs-understanding-test.html

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

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

相關文章

numpy 辨異(三)—— hstack/column_stack,linalg.eig/linalg.eigh

1. np.hstack np.column_stack >>> np.hstack([np.array([1, 2, 3]), np.array([4, 5, 6])]) array([1, 2, 3, 4, 5, 6])>>> np.column_stack([np.array([1, 2, 3]), np.array([4, 5, 6])]) array([[1, 4],[2, 5],[3, 6]]) 當然對等地,也存在&…

【代碼筆記】iOS-首頁3張圖片變化

一&#xff0c;效果圖。 二&#xff0c;工程圖。 三&#xff0c;代碼。 RootViewController.h #import <UIKit/UIKit.h>interface RootViewController : UIViewController {NSTimer *timer;UIImageView *imageView1;UIImageView *imageView2;UIImageView *imageView3;UIV…

acwing算法提高之動態規劃--數位DP

目錄 1 基礎知識2 模板3 訓練 1 基礎知識 暫無。。。 2 模板 暫無。。。 3 訓練 題目1&#xff1a;度的數量。 解題思路&#xff1a;分類討論。 C代碼如下&#xff0c; #include <iostream> #include <vector>using namespace std;const int N 35; int K,…

python 輸入數字變成密碼_如何在python中檢查數字的“密碼”

我建議使用sets和stdlib中的string包作為可接受字符的列表。在我還建議進行一點重構&#xff0c;以刪除大量帶有if / else分支的嵌套。在import stringupper set(list(string.uppercase))lower set(list(string.lowercase))numbers set(list(string.digits))while True:npw …

使用Eclipse在Amazon Ec2中部署Java Web應用程序的完整指南

嗨&#xff0c;讀者們&#xff0c; 今天&#xff0c;我將向您展示如何使用Eclipse IDE在Amazon EC2中部署簡單的Java Web應用程序。 在我們開始之前&#xff0c;我們需要一些必需的東西&#xff0c; Eclipse Java EE IDE –您可以從http://www.eclipse.org/downloads/下載&…

jquery的load方法

load方法指定一個界面會顯示在目標的標簽內部 比如MVC的一個分部視圖頁面想要顯示在某個標簽里面&#xff0c;可以寫成 $(標簽ID).load&#xff08;分部視圖名稱,data&#xff09; 其中第二個參數可選&#xff0c;主要是一些需要傳遞到該頁面的數據JSON格式組成&#xff0c;發送…

android 錄音原始文件_音頻采集:Android基于AudioRecord的實現

前言這篇文章簡單介紹下移動端Android系統下利用AudioRecord進行音頻采集方法。按照慣例開始前先提供一份源碼 AudioRecordLib 。AudioRecord采集的核心實現在于 AudioRecordCore.java 這個文件。權限申請想要使用AudioRecord這個API&#xff0c;需要在AndroidManifest.xml的配…

Spring 3和Java EE 6 –不公平和不完整的比較

這篇小文章的初稿標題為“ Spring&#xff06;Java EE –比較蘋果和橙子”。 在撰寫本文時&#xff0c;我了解到可以比較Spring Framework和Java EE&#xff0c;但這始終是不公平且不完整的工作。 Java for Enterprise和Spring Framework的發展緊密地聯系在一起。 兩者相互依存…

xml配置文件推薦方式

1.XML幫助類 /// <summary>/// Xml幫助類/// </summary>public class XmlHelper{/// <summary>/// 保存xml/// </summary>/// <typeparam name"T"></typeparam>/// <param name"path"></param>/// <p…

AFNetWorking https SSL認證

一般來講如果app用了web service , 我們需要防止數據嗅探來保證數據安全.通常的做法是用ssl來連接以防止數據抓包和嗅探 其實這么做的話還是不夠的 。 我們還需要防止中間人攻擊&#xff08;不明白的自己去百度&#xff09;。攻擊者通過偽造的ssl證書使app連接到了偽裝的假冒的…

查看環境列表_Xfce 4.14桌面環境正式發布,想要圖形界面又想節省內存?就它了...

1. Xfce 4.14桌面環境正式發布&#xff0c;它有什么新特性&#xff1f;本文主要講解Xfce 4.14桌面環境正式發布&#xff0c;它有什么新特性。Xfce已經開發了4年多&#xff0c;但是這個周末終于看到了期待已久的Xfce 4.14的發布。Xfce 4.14是這個輕量級桌面環境的最新穩定版本&a…

使用Log4jdbc記錄JDBC操作

當我們開發任何應用程序&#xff0c;完成它或結束其任何模塊時&#xff0c;我們都會開始優化過程。 大多數應用程序都包含數據庫訪問權限&#xff0c;并且如果您使用的是ORM &#xff0c;則可能會使用hibernate 。 優化休眠持久層&#xff0c;要求準備閱讀&#xff0c;理解和評…

android BluetoothAdapter藍牙BLE掃描總結

做室內定位的程序員應該都知道&#xff0c;在Android 5.0之后&#xff0c;google推出了藍牙掃描新接口&#xff0c;我們在實測中發現出一些問題&#xff0c;現在給大家列出&#xff0c;以供參考&#xff1a; 1.android 4.3.1(Build.VERSION_CODES.JELLY_BEAN_MR2)增加的startLe…

卷積神經網絡語音識別_用于物體識別的3D卷積神經網絡

本文提出了一種基于CNN的3D物體識別方法&#xff0c;能夠從3D圖像表示中識別3D物體&#xff0c;并在比較了不同的體素時的準確性。已有文獻中&#xff0c;3D CNN使用3D點云數據集或者RGBD圖像來構建3D CNNs&#xff0c;但是CNN也可以用于直接識別物體體積表示的體素。本文中&am…

段落排版--對齊(text-aliagn)

想為塊狀元素中的文本、圖片設置居中樣式嗎&#xff1f;可以使用text-align樣式代碼&#xff0c;如下代碼可實現文本居中顯示。(那么什么是塊狀元素呢&#xff1f;后面會講到呢~) h1{text-align:center; } <h1>了不起的蓋茨比</h1> 同樣可以設置居左&#xff1a; h…

Java 7:在不丟失數據的情況下關閉NIO.2文件通道

關閉異步文件通道可能非常困難。 如果您將I / O任務提交到異步通道&#xff0c;則需要確保正確執行了任務。 實際上&#xff0c;出于多種原因&#xff0c;這對于異步通道可能是一個棘手的要求。 默認的通道組使用守護進程線程作為工作線程&#xff0c;這不是一個好選擇&#xf…

JavaScript封裝方法,兼容參數類型為Number和String

/*** 依據Kind確定跳轉到目標列表頁面。* param kind*/function gobackByKind(kind) {var kindStr String(kind);switch(kindStr){case "1"://跳轉到客戶列表頁面window.location.href/biz/customer/list;break;case "2"://跳轉到代理機構列表頁面window.…

#獲得請求來源ip_以太網數據包TCP、IP、ICMP、UDP、ARP協議頭結構詳解

以太網首部目地MAC地址(8字節)源MAC地址(8字節)類型(2字節)1、IP頭的結構版本(4位)頭長度(4位)服務類型(8位)封包總長度(16位)封包標識(16位)標志(3位)片斷偏移地址(13位)存活時間(8位)協議(8位)校驗和(16位)來源IP地址(32位)目的IP地址(32位)選項(可選)填充(可選)數據(1)字節和…

團隊項目第二次沖刺Ⅶ

今天將整體代碼的編碼方式改了&#xff0c;作業模塊基本修改完成 遇到的問題是對于添加問答模塊無從下手轉載于:https://www.cnblogs.com/brucekun/p/5573312.html

編寫Play 2的模塊,第2部分:攔截器

在本教程的第一部分中&#xff0c;我們介紹了創建&#xff0c;發布和調用模塊的基本知識。 我們創建的模塊并沒有真正做很多事情&#xff0c;因此現在是時候使用Play的某些功能來擴展功能了。 1.攔截器 攔截器使您可以攔截對控制器的調用&#xff0c;并增強或阻止其行為。 在第…