下載地址:html
http://ocmock.org/download/ios
源碼及示例下載地址:git
https://github.com/erikdoe/ocmockgithub
項目配置ocmock地址:數據庫
http://ocmock.org/ios/網絡
ocmock features:異步
http://ocmock.org/features/函數
什麼是mock?差很少就是紙老虎
單元測試
當 咱們寫單元測試的時候,不可避免的要去儘量少的實例化一些具體的組件來保持測試既短又快。並且保持單元的隔離。在現代的面向對象系統中,測試的組件很可 能會有幾個依賴的對象。咱們用mock來替代實例化具體的依賴class。mock是在測試中的一個僞造的有預約義行爲的具體對象的替身對象。被測試的組 件不知道其中的差別!你的組件是在一個更大的系統中被設計的,你能夠頗有信心的用mock來測試你的組件。學習
常見的mock使用案例
stub方法
咱們用一個簡單的例子來開始解釋OCMock中通常的stub語法。
1
2
3
|
id jalopy = [OCMock mockForClass[Car class]];
[[[jalopy stub] andReturn:@
"75kph"
] goFaster:[OCMArg any] units:@
"kph"
];
// if returning a scalar value, andReturnValue: can be used
|
OCMock3 新版本對應API
1
2
3
|
id jalopy = OCMStrictClassMock([Car class]);
OCMStub([jalopy goFaster:[OCMArg any] units:@
"kph"
]).andReturn(@
"75kph"
);
// if returning a scalar value, andReturnValue: can be used
|
這個簡單的例子首先從Car類中mock出一個jalopy(老爺車),而後,stub掉goFaster:方法讓它返回字符串@」75kph」。stub語法可能看起來有點奇怪,但這是廣泛的作法:
1
|
ourMockObject stub] whatItShouldReturn ] method:
|
OCMock3 新版本對應API
1
|
OCMStub([ourMockObject method:]).andReturn()
|
一個很是重要的說明:注意[OCMArg any]的用法。當指定一個帶參數的方法時,方法被調用而且參數爲指定參數的話,mock會返回andReturn:指定的值。[OCMArg any]方法告訴stub匹配全部的參數值。舉個例子:
1
|
[car goFaster:84 units:@
"mph"
];
|
不會觸發stub,由於最後一個參數不匹配」kph」.
類方法
OCMock會在mock實例上沒有找到相同名字的實例方法的時候去找同名的類方法。在名字相同的狀況下(類方法和實例方法同名),用classMethod來指定類方法:
1
|
[[[[jalopy stub] classMethod] andReturn:@
"expired"
] checkWarrany];
|
在OCMock3中classMethod和instanceMethod的stub方式同樣,例如:
1
2
3
4
|
id classMock = OCMClassMock([SomeClass class]);
OCMStub([classMock aClassMethod]).andReturn(@
"Test string"
);
// result is @"Test string"
NSString *result = [SomeClass aClassMethod];
|
mock類型 – niceMock,partialMock
OCMock提供了幾種不一樣類型的mock,每一個都有他們特定的使用場景。
用這種方式來建立任意mock:
1
|
id mockThing = [OCMock mockForClass[Thing class]];
|
OCMock3 新版本對應API
1
|
id mockThing = OCMStrictClassMock([Thing class]);
|
這就是我所說的‘vanilla’ mock。‘vanilla’ mock當調用一個沒有stub的方法的時候會拋出一個異常。這會獲得一個單調的mock,且在mock的生命週期中每個方法調用都要被stub掉。(更多信息請看下一節關於stub)
若是你不想stub不少方法,用‘nice’ mock。‘nice’ mock很是有禮貌並且不會在一個沒有stub掉的方法被調用的時候拋出異常。
1
|
id niceMockThing = [OCMock niceMockForClass[Thing class]];
|
OCMock3 新版本對應API
1
|
id mockThing = OCMClassMock([Thing class]);
|
最後一個mock類型是‘partial’ mock。當一個沒有stub掉的方法被調用了,這個方法會被轉發到真實的對象上。這是對mock技術上的欺騙,可是很是有用,當有一些類不適合讓本身很好的被stub。
1
2
|
Thing *someThing = [Thing alloc] init];
id aMock = [OCMockObject partialMockForObject:someThing]
|
OCMock3 新版本對應API
1
2
|
Thing *someThing = [Thing alloc] init];
id aMock = OCMPartialMock(someThing);
|
驗證方法是否被調用
驗證方法是否被調用很是簡單。這個能夠用expect來完成拒絕和驗證方法:
1
2
3
4
|
id niceMockThing = [OCMock niceMockForClass[Thing class]];
[[niceMockThing expect] greeting:@
"hello"
];
// verify the method was called as expected
[niceMocking verify];
|
OCMock3 新版本對應API
1
2
|
id niceMockThing = OCMClassMock([Thing class]);
OCMVerify([niceMockThing greeting:@
"hello"
]);
|
當 被驗證的方法沒有被調用的時候會拋出異常。若是你用的是XCTest,那麼請用XCTAssertNotThrow來包裝驗證調用。拒絕方法調用也是一樣 的道理,可是會再方法調用的時候拋出異常。就像stub,selector和傳遞過去驗證的參數必須匹配調用時候傳遞過去的參數。用[OCMArg any]能夠簡化咱們的工做。
處理block參數
OCMock也能夠處理block回調參數。block回調一般用於網絡代碼,數據庫代碼,或者在任何異步操做中。在這個例子中,思考下下面的方法:
1
2
|
- (void)downloadWeatherDataForZip:(NSString *)zip
callback:(void (^)(NSDictionary *response))callback;
|
在這個例子中,咱們有一個下載天氣壓縮數據的方法,而且把下載下來的dictionary代理到一個block的回調中。在測試中,咱們經過預約義的天氣數據來測試回調處理。這也是明智的測試失敗場景。你永遠不會知道網絡上會返回你什麼東西!
1
2
3
4
5
6
7
8
9
10
|
// 1. stub using OCMock andDo: operator.
[[[groupModelMock stub] andDo:^(NSInvocation *invoke) {
//2. declare a block with same signature
void (^weatherStubResponse)(NSDictionary *dict);
//3. link argument 3 with with our block callback
[invoke getArgument:&weatherStubResponse atIndex:3];
//4. invoke block with pre-defined input
NSDictionary *testResponse = @{@
"high"
: 43 , @
"low"
: 12};
weatherStubResponse(groupMemberMock);
}]downloadWeatherDataForZip@
"80304"
callback:[OCMArg any] ];
|
OCMock3 新版本對應API
1
2
3
4
5
6
7
8
9
10
|
// 1. stub using OCMock andDo: operator.
OCMStub([groupModelMock downloadWeatherDataForZip:@
"80304"
callback:[OCMArg any]]]).andDo(^(NSInvocation *invocation){
//2. declare a block with same signature
void (^weatherStubResponse)(NSDictionary *dict);
//3. link argument 3 with with our block callback
[invoke getArgument:&weatherStubResponse atIndex:3];
//4. invoke block with pre-defined input
NSDictionary *testResponse = @{@
"high"
: 43 , @
"low"
: 12};
weatherStubResponse(groupMemberMock);
});
|
這裏的大致思想至關簡單,即使如此,他的實現也須要一些說明:
1. 這個mock對象使用帶NSInvocation參數的「andDo」方法。一個NSInvocation對象表明一個 ‘objectivetified’(實在不知道這個什麼鬼)表現的方法調用。經過這個NSinvocation對象,使得攔截傳遞給咱們的方法的 block參數變得可能。
2.用與咱們測試的方法中相同的方法簽名聲明一個block參數。
3.NSInvocation實 例方法"getArgument:atIndex:"將賦值後的塊函數傳遞都原始函數中定義的塊函數中。注意:在Objective-C中,傳遞給任意方 法的前兩個參數都是「self」和「_cmd」.這是一個運行時的小功能以及用下標來獲取NSInvocation參數時咱們須要考慮的東西。
4.最後,傳遞這個回調的預約義字典。
最後
但願這篇文章和例子已經陳述清楚一些OCMock最通用的用法。OCMock站點:http://ocmock.org/features/是一個最好的學習OCMock的地方。mock是單調的可是對於一個現代的OO系統倒是必須的。若是一個依賴圖很難用mock來測試,這個跡象代表你的設計須要從新考慮了。
轉自:http://www.cocoachina.com/ios/20150508/11769.html