編輯
只是看着更加緊密地在OCMock的最新版本,我認爲你是在解釋嘲笑一個類的方法,你直接測試的方式是正確的,所以是問題僅僅是你用錯誤的簽名來調用它?
下面的答案是一般情況(當被測方法調用類方法時也很有用)。
原始
檢查一類方法與OCMock是有點棘手。
你目前正在做的是創建一個模擬
對象叫做detailMock和存根
實例方法叫做getBoolVal:
(順便說一下,你的方法原型不帶參數,所以你不應該傳遞nil給如果你想遵循Apple的指導方針,他們建議不要在getter中使用「get」這個詞(除非你發送一個指針參考來獲取set))。編譯不會失敗,因爲detailMock是一個id,並且願意響應任何選擇器。
那麼如何測試一個Class方法呢?對於一般情況,你需要做一些調整。這是我如何做到的。
讓我們看看我們如何僞造NSURLConnection
,您應該可以將它應用到您的課堂。
開始通過擴展類:
@interface FakeNSURLConnection : NSURLConnection
+ (id)sharedInstance;
+ (void)setSharedInstance:(id)sharedInstance;
+ (void)enableMock:(id)mock;
+ (void)disableMock;
- (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate;
@end
注意,我感興趣的測試connectionWithRequest:委託和我已經擴展的類增加一個公共實例方法具有相同的簽名類方法。讓我們看看執行情況:
@implementation FakeNSURLConnection
SHARED_INSTANCE_IMPL(FakeNSURLConnection);
SWAP_METHODS_IMPL(NSURLConnection, FakeNSURLConnection);
DISABLE_MOCK_IMPL(FakeNSURLConnection);
ENABLE_MOCK_IMPL(FakeNSURLConnection);
+ (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate {
return [FakeNSURLConnection.sharedInstance connectionWithRequest:request delegate:delegate];
}
- (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate { return nil; }
@end
那麼這裏發生了什麼?首先我會在下面討論一些宏。接下來,我重寫了類方法,讓它調用實例方法。我們可以使用OCMock
來模擬實例方法,所以通過讓類方法調用實例方法,我們可以讓類方法調用模擬。
我們不希望在我們的實際代碼使用FakeNSURLConnection雖然,但我們希望在我們的測試中使用它。我們應該怎麼做?我們可以調酒類NSURLConnection
和FakeNSURLConnection
之間的方法。這意味着,以後我們調酒與呼叫FakeNSURLConnection connectionWithRequest:delegate
到NSURLConnection connectionWithRequest:delegate
通話。這將我們帶到我們的宏:
#define SWAP_METHODS_IMPL(REAL, FAKE) \
+ (void)swapMethods \
{ \
Method original, mock; \
unsigned int count; \
Method *methodList = class_copyMethodList(object_getClass(REAL.class), &count); \
for (int i = 0; i < count; i++) \
{ \
original = class_getClassMethod(REAL.class, method_getName(methodList[i])); \
mock = class_getClassMethod(FAKE.class, method_getName(methodList[i])); \
method_exchangeImplementations(original, mock); \
} \
free(methodList); \
}
#define DISABLE_MOCK_IMPL(FAKE) \
+ (void)disableMock \
{ \
if (_mockEnabled) \
{ \
[FAKE swapMethods]; \
_mockEnabled = NO; \
} \
}
#define ENABLE_MOCK_IMPL(FAKE) \
static BOOL _mockEnabled = NO; \
+ (void)enableMock:(id)mockObject; \
{ \
if (!_mockEnabled) \
{ \
[FAKE setSharedInstance:mockObject]; \
[FAKE swapMethods]; \
_mockEnabled = YES; \
} \
else \
{ \
[FAKE disableMock]; \
[FAKE enableMock:mockObject]; \
} \
}
#define SHARED_INSTANCE_IMPL() \
+ (id)sharedInstance \
{ \
return _sharedInstance; \
}
#define SET_SHARED_INSTANCE_IMPL() \
+ (void)setSharedInstance:(id)sharedInstance \
{ \
_sharedInstance = sharedInstance; \
}
我會推薦這樣的東西,所以你不會無意中重新調整你的類方法。那麼你會如何使用它?
id urlConnectionMock = [OCMockObject niceMockForClass:FakeNSURLConnection.class];
[FakeNSURLConnection enableMock:urlConnectionMock];
[_mocksToDisable addObject:FakeNSURLConnection.class];
[[[urlConnectionMock expect] andReturn:urlConnectionMock] connectionWithRequest:OCMOCK_ANY delegate:OCMOCK_ANY];
這幾乎就是了 - 你已經調整了方法,所以你的假類會被調用,這將叫你的模擬。
啊,但最後一件事。 _mocksToDisable是一個NSMutableArray
,它將包含我們調試過的每個類的類對象。
- (void)tearDown
{
for (id mockToDisable in _mocksToDisable)
{
[mockToDisable disableMock];
}
}
我們這樣做是tearDown
以確保我們unswizzled我們班的測試運行後 - 不這樣做是正確的測試,如果有因異常並不是所有的測試代碼可以得到執行但撕毀總是會的。
可能有其他的模擬技術,使這個簡單,但我發現它不是那麼糟糕,因爲你寫了一次,可以使用很多次。