[iOS單元測試系列]-譯-OCMock常見使用方式

該文章使用的API是OCMock老版本的API妒穴,新版本也兼容老版本的API流强,譯者在用到老版本的API處已經(jīng)添加了對應(yīng)的新版本(OCMock3)的API供讀者參考。

愛好者

這篇文章假設(shè)讀者都能熟悉使用Xcode5的測試框架XCTest,或者BBD測試工具Kiwi或其他的iOS測試框架

什么是mock?差不多就是紙老虎

當我們寫單元測試的時候舆吮,不可避免的要去盡可能少的實例化一些具體的組件來保持測試既短又快。而且保持單元的隔離队贱。在現(xiàn)代的面向?qū)ο笙到y(tǒng)中色冀,測試的組件很可能會有幾個依賴的對象。我們用mock來替代實例化具體的依賴class柱嫌。mock是在測試中的一個偽造的有預(yù)定義行為的具體對象的替身對象锋恬。被測試的組件不知道其中的差異!你的組件是在一個更大的系統(tǒng)中被設(shè)計的慎式,你可以很有信心的用mock來測試你的組件伶氢。

常見的mock使用案例

stub方法

我們用一個簡單的例子來開始解釋OCMock中一般的stub語法趟径。

123

idjalopy=[OCMockmockForClass[Carclass]];[[[jalopystub]andReturn:@"75kph"]goFaster:[OCMArgany]units:@"kph"];// if returning a scalar value, andReturnValue: can be used

OCMock3 新版本對應(yīng)API

123

idjalopy=OCMStrictClassMock([Carclass]);OCMStub([jalopygoFaster:[OCMArgany]units:@"kph"]).andReturn(@"75kph");// if returning a scalar value, andReturnValue: can be used

這個簡單的例子首先從Car類中mock出一個jalopy(老爺車),然后瘪吏,stub掉goFaster:方法讓它返回字符串@”75kph”。stub語法可能看起來有點奇怪蜗巧,但這是普遍的做法:

ourMockObject stub] whatItShouldReturn ] method:

OCMock3 新版本對應(yīng)API

OCMStub([ourMockObject method:]).andReturn()

一個非常重要的說明:注意[OCMArg any]的用法掌眠。當指定一個帶參數(shù)的方法時,方法被調(diào)用并且參數(shù)為指定參數(shù)的話幕屹,mock會返回andReturn:指定的值蓝丙。[OCMArg any]方法告訴stub匹配所有的參數(shù)值。舉個例子:

[car goFaster:84 units:@"mph"];

不會觸發(fā)stub望拖,因為最后一個參數(shù)不匹配”kph”.

類方法

OCMock會在mock實例上沒有找到相同名字的實例方法的時候去找同名的類方法渺尘。在名字相同的情況下(類方法和實例方法同名),用classMethod來指定類方法:

[[[[jalopy stub] classMethod] andReturn:@"expired"] checkWarrany];

在OCMock3中classMethod和instanceMethod的stub方式一樣说敏,例如:

1234

idclassMock=OCMClassMock([SomeClassclass]);OCMStub([classMockaClassMethod]).andReturn(@"Test string");// result is @"Test string"NSString*result=[SomeClassaClassMethod];

mock類型 – niceMock,partialMock

OCMock提供了幾種不同類型的mock鸥跟,每個都有他們特定的使用場景。

用這種方式來創(chuàng)建任意mock:

id mockThing = [OCMock mockForClass[Thing class]];

OCMock3 新版本對應(yīng)API

id mockThing = OCMStrictClassMock([Thing class]);

這就是我所說的‘vanilla’ mock盔沫∫阶桑‘vanilla’ mock當調(diào)用一個沒有stub的方法的時候會拋出一個異常。這會得到一個單調(diào)的mock架诞,且在mock的生命周期中每一個方法調(diào)用都要被stub掉拟淮。(更多信息請看下一節(jié)關(guān)于stub)

如果你不想stub很多方法,用‘nice’ mock谴忧『懿矗‘nice’ mock非常有禮貌而且不會在一個沒有stub掉的方法被調(diào)用的時候拋出異常角虫。

id niceMockThing = [OCMock niceMockForClass[Thing class]];

OCMock3 新版本對應(yīng)API

id mockThing = OCMClassMock([Thing class]);

最后一個mock類型是‘partial’ mock。當一個沒有stub掉的方法被調(diào)用了撑蚌,這個方法會被轉(zhuǎn)發(fā)到真實的對象上上遥。這是對mock技術(shù)上的欺騙,但是非常有用争涌,當有一些類不適合讓自己很好的被stub粉楚。

12

Thing*someThing=[Thingalloc]init];idaMock=[OCMockObjectpartialMockForObject:someThing]

OCMock3 新版本對應(yīng)API

12

Thing*someThing=[Thingalloc]init];idaMock=OCMPartialMock(someThing);

驗證方法是否被調(diào)用

驗證方法是否被調(diào)用非常簡單。這個可以用expect來完成拒絕和驗證方法:

1234

idniceMockThing=[OCMockniceMockForClass[Thingclass]];[[niceMockThingexpect]greeting:@"hello"];// verify the method was called as expected[niceMockingverify];

OCMock3 新版本對應(yīng)API

12

idniceMockThing=OCMClassMock([Thingclass]);OCMVerify([niceMockThinggreeting:@"hello"]);

當被驗證的方法沒有被調(diào)用的時候會拋出異常亮垫。如果你用的是XCTest模软,那么請用XCTAssertNotThrow來包裝驗證調(diào)用。拒絕方法調(diào)用也是同樣的道理饮潦,但是會再方法調(diào)用的時候拋出異常燃异。就像stub,selector和傳遞過去驗證的參數(shù)必須匹配調(diào)用時候傳遞過去的參數(shù)继蜡。用[OCMArg any]可以簡化我們的工作回俐。

處理block參數(shù)

OCMock也可以處理block回調(diào)參數(shù)。block回調(diào)通常用于網(wǎng)絡(luò)代碼稀并,數(shù)據(jù)庫代碼仅颇,或者在任何異步操作中。在這個例子中碘举,思考下下面的方法:

12

-(void)downloadWeatherDataForZip:(NSString*)zipcallback:(void(^)(NSDictionary*response))callback;

在這個例子中忘瓦,我們有一個下載天氣壓縮數(shù)據(jù)的方法,并且把下載下來的dictionary代理到一個block的回調(diào)中引颈。在測試中耕皮,我們通過預(yù)定義的天氣數(shù)據(jù)來測試回調(diào)處理。這也是明智的測試失敗場景蝙场。你永遠不會知道網(wǎng)絡(luò)上會返回你什么東西凌停!

12345678910

// 1. stub using OCMock andDo: operator.[[[groupModelMockstub]andDo:^(NSInvocation*invoke){//2. declare a block with same signaturevoid(^weatherStubResponse)(NSDictionary*dict);//3. link argument 3 with with our block callback[invokegetArgument:&weatherStubResponseatIndex:3];//4. invoke block with pre-defined inputNSDictionary*testResponse=@{@"high":43,@"low":12};weatherStubResponse(groupMemberMock);}]downloadWeatherDataForZip@"80304"callback:[OCMArgany]];

OCMock3 新版本對應(yīng)API

12345678910

// 1. stub using OCMock andDo: operator.OCMStub([groupModelMockdownloadWeatherDataForZip:@"80304"callback:[OCMArgany]]]).andDo(^(NSInvocation*invocation){//2. declare a block with same signaturevoid(^weatherStubResponse)(NSDictionary*dict);//3. link argument 3 with with our block callback[invokegetArgument:&weatherStubResponseatIndex:3];//4. invoke block with pre-defined inputNSDictionary*testResponse=@{@"high":43,@"low":12};weatherStubResponse(groupMemberMock);});

這里的大體思想相當簡單,即便如此售滤,他的實現(xiàn)也需要一些說明:

1.這個mock對象使用帶NSInvocation參數(shù)的“andDo”方法罚拟。一個NSInvocation對象代表一個‘objectivetified’(實在不知道這個什么鬼)表現(xiàn)的方法調(diào)用。通過這個NSinvocation對象趴泌,使得攔截傳遞給我們的方法的block參數(shù)變得可能舟舒。

2.用與我們測試的方法中相同的方法簽名聲明一個block參數(shù)。

3.NSInvocation實例方法"getArgument:atIndex:"將賦值后的塊函數(shù)傳遞都原始函數(shù)中定義的塊函數(shù)中嗜憔。注意:在Objective-C中秃励,傳遞給任意方法的前兩個參數(shù)都是“self”和“_cmd”.這是一個運行時的小功能以及用下標來獲取NSInvocation參數(shù)時我們需要考慮的東西。

4.最后吉捶,傳遞這個回調(diào)的預(yù)定義字典夺鲜。

最后

希望這篇文章和例子已經(jīng)陳述清楚一些OCMock最通用的用法皆尔。OCMock站點:http://ocmock.org/features/是一個最好的學(xué)習OCMock的地方。mock是單調(diào)的但是對于一個現(xiàn)代的OO系統(tǒng)卻是必須的币励。如果一個依賴圖很難用mock來測試慷蠕,這個跡象表明你的設(shè)計需要重新考慮了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末食呻,一起剝皮案震驚了整個濱河市流炕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌仅胞,老刑警劉巖每辟,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異干旧,居然都是意外死亡渠欺,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門椎眯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挠将,“玉大人,你說我怎么就攤上這事编整√蛳。” “怎么了?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵闹击,是天一觀的道長镶蹋。 經(jīng)常有香客問我成艘,道長赏半,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任淆两,我火速辦了婚禮断箫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘秋冰。我一直安慰自己仲义,他們只是感情好,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布剑勾。 她就那樣靜靜地躺著埃撵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪虽另。 梳的紋絲不亂的頭發(fā)上暂刘,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天,我揣著相機與錄音捂刺,去河邊找鬼谣拣。 笑死募寨,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的森缠。 我是一名探鬼主播拔鹰,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼贵涵!你這毒婦竟也來了列肢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤宾茂,失蹤者是張志新(化名)和其女友劉穎例书,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體刻炒,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡决采,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了坟奥。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片树瞭。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖爱谁,靈堂內(nèi)的尸體忽然破棺而出晒喷,到底是詐尸還是另有隱情,我是刑警寧澤访敌,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布凉敲,位于F島的核電站,受9級特大地震影響寺旺,放射性物質(zhì)發(fā)生泄漏爷抓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一阻塑、第九天 我趴在偏房一處隱蔽的房頂上張望蓝撇。 院中可真熱鬧,春花似錦陈莽、人聲如沸渤昌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽独柑。三九已至,卻和暖如春私植,著一層夾襖步出監(jiān)牢的瞬間忌栅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工兵琳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留狂秘,地道東北人骇径。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像者春,于是被迫代替她去往敵國和親破衔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容