iOS中的測試:OCMock

Mock介紹

什么是mock測試?

對于一些不容易構造或不容易獲取的對象,此時你可以創建一個虛擬的對象(mock object)來完成測試。

例如你可能要嘗試100次才會返回一個NSError,通過mock object你可以自行創建一個NSError對象,測試在出錯情況下程序的處理是否符合你的預期。

例如你要連接服務器但是服務器在實驗室,你在外工作的時候就無法測試了(小弟就試過這種情況,非常反感),這個時候你可以創建一個虛擬的服務器,并返回一些你指定的數據,從而繞過服務器。

例如假設你要訪問一個數據庫,但是訪問過程的開銷巨大,這時你可以虛擬一個數據庫,并且返回一些自行定制的數據,從而繞過了數據庫的訪問。

mock的思想很簡單:沒有條件?我們就自行創造條件。

OCMock介紹

OCMock是一個用于為iOS或Mac OS X項目配置Mock測試的開源項目。

其實現思想就是根據要mock的對象的class來創建一個對應的對象,并且設置好該對象的屬性和調用預定方法后的動作(例如返回一個值,調用代碼塊,發送消息等等),然后將其記錄到一個數組中,接下來開發者主動調用該方法,最后做一個verify(驗證),從而判斷該方法是否被調用,或者調用過程中是否拋出異常等。

其實就是可以把它當做我們偽造的一個對象,我們給它一些預設的值之類的,然后就可以進行對應的驗證了。

配置

OCMock的官網下載dmg文件,打開后里面有個iOS library文件夾。把iOS library里的文件加入到你的項目里,按這篇教程來進行配置。

OCMock文檔,可以到這里查看如何詳細使用OCMock。

例子中使用到的方法說明

(以下數字為OCMock文檔中的數字目錄)

1.1. Class mocks

id classMock = OCMClassMock([SomeClass class]);

創建mock object當做類的實例。

2.1. Stubbing methods that return objects

OCMStub([mock someMethod]).andReturn(anObject);

告訴mock object當它調用someMethod的時候應該返回anObject。

3.1. Verify-after-running

id mock = OCMClassMock([SomeClass class]);

/* run code under test */

OCMVerify([mock someMethod]);

/* run code under test */寫一些對應的代碼,然后 OCMVerify([mock someMethod]);驗證方法是否被調用。

3.2. Stubs and verification

id mock = OCMClassMock([SomeClass class]);
OCMStub([mock someMethod]).andReturn(myValue);

/* run code under test */

OCMVerify([mock someMethod]);

4.1. The any constraint

OCMStub([mock someMethodWithAnArgument:[OCMArg any]])
OCMStub([mock someMethodWithPointerArgument:[OCMArg anyPointer]])
OCMStub([mock someMethodWithSelectorArgument:[OCMArg anySelector]])

實際例子

以下的例子Demo:DSOCMockDemo

例子1

使用了文檔中的OCMock文檔中以下幾種方法:
1.1 Class mocks
2.1 Stubbing methods that return objects
3.1 Verify-after-running
3.2 Stubs and verification
4.1 The any constraint

這是一個來自于OCMock上的例子。我這里就做一下翻譯和Demo。

為了使我們更加具體的理解使用OCMock,這里假設我們寫了一個接收來自于Twitter信息的應用。

有一個TwitterViewController類,一個TwitterConnection用來調用Twitter API得到數據,和一個TweetView類用來顯示tweet對象。

下面是TwitterViewController類,有connectiontweetView對象。

@interface TwitterViewController : UIViewController

@property(nonatomic, strong)TwitterConnection *connection;
@property(nonatomic, strong)TweetView         *tweetView;

- (void)updateTweetView;

@end

TwitterConnection是一個網絡連接類,有一個fetchTweets方法用來接收請求回來的信息。返回一個Tweet對象的數組。

@interface TwitterConnection : NSObject

- (NSArray *)fetchTweets;

@end

TweetView有一個addTweet:方法用來添加每一個Tweet對象到頁面上。

@interface TweetView : UIView

- (void)addTweet:(Tweet *)aTweet;

@end

為什么使用mocks用來測試

當我們為updateTweetView來寫一個測試類的時候我們要考慮它有哪些相對應的依賴,也就是TwitterConnection和TweetView。在這個例子里我們需要實例化一個真正的TwitterConnection對象來請求真實數據然后使用它。這樣的話會有幾個問題:

  • 使用真實的connection會使測試變慢,因為它還要去請求網絡。
  • 我們永遠不知道每一次Twitter返回的數據是什么。
  • 很難測試錯誤的返回,因為Twitter一般不會返回錯誤。

解決的方法就是偽造一個假的connection,既一個stub。

下圖就是使用測試的代碼:

例子2

OCMock會在mock實例上沒有找到相同名字的實例方法的時候去找同名的類方法。

使用例子一種的類,給TwitterConnection加個類方法
+ (NSArray *)fetchTweets2;

我們可以看到例子2和例子中classMethod和instanceMethod的stub方式一樣。

例子3

使用了文檔中的OCMock文檔中以下幾種方法:
1.3 Strict class and protocol mocks
7.1 Expect-run-verify
7.2 Strict mocks and failing fast
7.3 Stub actions and expect

當我們使用普通的mock的時候是這樣的:

- (void)testStrictMock3{

    id classMock = OCMClassMock([TweetView class]);
    
    //設置期望或預設,這個classMock需要執行addTweet方法且參數不為nil。  不然的話會拋出異常
    //OCMExpect([classMock addTweet:[OCMArg isNotNil]]);
    //OCMStub([classMock addTweet:[OCMArg isNotNil]]);

    /* 如果不執行以下代碼的話會拋出異常 */
    Tweet *testTweet = [[Tweet alloc] init];
    testTweet.userName = @"齊滇大圣";
    [classMock addTweet:testTweet];

    OCMVerifyAll(classMock);
}

這表示一種友好的mock,不會在沒有OCMExpect或OCMStub設置類的所有方法時拋出異常。以上代碼把OCMExpect和OCMStub注釋掉時不會報錯。

還有一種表示嚴格的mock:OCMStrictClassMock,如果把OCMExpect和OCMStub注釋掉時會報錯,它要求你執行類中的所有方法,所以比較適合用來測試必須實現的方法,代碼如下:

- (void)testStrictMock3{
    id classMock = OCMStrictClassMock([TweetView class]);
    //OCMExpect([classMock addTweet:[OCMArg isNotNil]]);
    //OCMStub([classMock addTweet:[OCMArg isNotNil]]);

    Tweet *testTweet = [[Tweet alloc] init];
    testTweet.userName = @"齊滇大圣";
    [classMock addTweet:testTweet]; 

    OCMVerifyAll(classMock);
}

參考

ocmock源碼
[iOS單元測試系列]單元測試編碼規范
[iOS單元測試系列]-譯-OCMock常見使用方式
Introduction to mocking with OCMock

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容