背景
在Linux后臺服務(wù)類(lèi)模塊測試中,經(jīng)常會(huì )遇到被測模塊需要通過(guò)socket接口調用其它模塊的情況,多數時(shí)候,我們可以直接連接被調用的模塊來(lái)進(jìn)行測試。但有時(shí)這并不是個(gè)好主意,比如被調用的模塊部署成本很高、操作比較繁瑣、數據構造困難、性能不夠好等,更重要的是一些接口的異常情況可能根本無(wú)法直接模擬。
所以在實(shí)際測試中,我們少不了要自己編寫(xiě)一些樁程序來(lái)模擬被調用模塊的行為。而當我們寫(xiě)過(guò)幾個(gè)樁程序后就會(huì )發(fā)現,所有的樁程序都大同小異,只是具體的接口協(xié)議不同而已,而像鏈接管理、配置管理、日志管理等工作,完全都是一樣的。那么是否可以通過(guò)某種方式,將相同的部分抽離出來(lái),構造一個(gè)樁程序時(shí),只需要考慮接口的邏輯,是不是就可以節省許多重復的工作呢?
什么是MOCK
Mock原本是一種在單測中使用的測試技術(shù)。
Mock的定義
“Mock Objects simulate parts of the behavior of domain code, and are able to check whether they are used as defined. Domain classes can be tested in isolation by simulating their collaborators with Mock Objects.”。前面這段話(huà)摘自EasyMock的說(shuō)明文檔,簡(jiǎn)單來(lái)說(shuō)單測CASE可以認為是一些驅動(dòng)代碼,而Mock Object則像是一些樁。
Mock框架簡(jiǎn)介
在實(shí)際使用中,自己從頭實(shí)現一個(gè)Mock對象是件繁瑣的工作,而且經(jīng)常出現各種低級錯誤,影響實(shí)際使用的效果。所以一些通用的Mock框架應運而生。如 “EasyMock”、“GMock”等。
“Hand-writing classes for Mock Objects is not needed.
Supports refactoring-safe Mock Objects: test code will not break at runtime when renaming methods or reordering method parameters
Supports return values and exceptions.
Supports checking the order of method calls, for one or more Mock Objects. ”
上面是EasyMock文檔中提到的Mock框架帶來(lái)的幾點(diǎn)好處,總結一下就是:可以使用一種非常簡(jiǎn)潔的方式描述對象的行為,而不需要真的去實(shí)現它。
關(guān)于EasyMock的詳細信息,可以參考 EasyMock的使用文檔
Mock在單測中的應用
使用Mock進(jìn)行單測,大體上有以下幾個(gè)方面(摘自EasyMock官方文檔)
Define Interface
public interface Collaborator { void documentAdded(String title); void documentChanged(String title); void documentRemoved(String title); byte voteForRemoval(String title); byte[] voteForRemovals(String[] title); }
Define a Model Class
public class ClassUnderTest { // … public void addListener(Collaborator listener) { // … } public void addDocument(String title, byte[] document) { // … } public boolean removeDocument(String title) { // … } public boolean removeDocuments(String[] titles) { // … } }
Create a Mock Object
protected void setUp() { mock = createMock(Collaborator.class); // 1 classUnderTest = new ClassUnderTest(); classUnderTest.addListener(mock);} public void testRemoveNonExistingDocument() { // 2 (we do not expect anything) replay(mock); // 3 classUnderTest.removeDocument(“Does not exist“);}
Adding Behavior
public void testAddDocument() { mock.documentAdded(“New Document“); // 2 replay(mock); // 3 classUnderTest.addDocument(“New Document“, new byte[0]); }
Specifying Return Values
public void testVoteForRemoval() { mock.documentAdded(“Document“); // expect document addition // expect to be asked to vote for document removal, and vote for it expect(mock.voteForRemoval(“Document“)).andReturn((byte) 42); mock.documentRemoved(“Document“); // expect document removal replay(mock); classUnderTest.addDocument(“Document“, new byte[0]); assertTrue(classUnderTest.removeDocument(“Document“)); verify(mock);}
什么是Mock Server
前面說(shuō)了好多什么是Mock,或者說(shuō)什么是Mock Object,那什么是Mock Server呢?其實(shí)它相對于我們一直使用的樁程序來(lái)說(shuō)的,為了方便,下文將其稱(chēng)為Stub Server。
MockServer的工作原理
一般樁程序的結構
首先,我們先回顧一下以往的Stub Server是什么樣的
Stub Server,作為一個(gè)模擬下游模塊的行為的程序,它的功能無(wú)非兩方面,一個(gè)是接受請求,一個(gè)是返回結果。當然為測試的便利,在返回結果時(shí)可能還會(huì )有一些簡(jiǎn)單的邏輯,比如填充一些無(wú)關(guān)字段。
對于一個(gè)標準的Stub Server,它的結構通常是:
原文轉自:http://kjueaiud.com