2013-05-09 67 views
0

我想檢查一下是否使用OCMock調用類方法。我從OCMock網站和其他關於SO的其他答案中收集了新的OCMock版本(2.1)添加了對存根類方法的支持。使用OCMock版本的測試類方法2.1.1

我試圖做同樣的:

DetailViewController:

+(BOOL)getBoolVal 
{ 
return YES; 
} 

測試用例:

-(void) testClassMethod 
{ 
id detailMock = [OCMockObject mockForClass:[DetailViewController class]]; 

[[[detailMock stub] andReturnValue:OCMOCK_VALUE((BOOL){YES})] getBoolVal:nil]; 
} 

的測試運行,併成功,但成功的,即使我返回NO而不是在DetailViewControllergetBoolVal方法中爲YES。在保持該方法的斷點時,測試執行不會停止指示該方法未被調用。

那麼我該如何檢查一個類的方法呢?

回答

0

編輯

只是看着更加緊密地在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雖然,但我們希望在我們的測試中使用它。我們應該怎麼做?我們可以調酒NSURLConnectionFakeNSURLConnection之間的方法。這意味着,以後我們調酒與呼叫FakeNSURLConnection connectionWithRequest:delegateNSURLConnection 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我們班的測試運行後 - 不這樣做是正確的測試,如果有因異常並不是所有的測試代碼可以得到執行但撕毀總是會的。

可能有其他的模擬技術,使這個簡單,但我發現它不是那麼糟糕,因爲你寫了一次,可以使用很多次。

0

也許我失去了一些東西(我知道這是一個有點老了),但你的類簽名是+(BOOL)getBoolVal { return YES; },並在您的測試,你打電話期望在getBoolVal:nil

那些不匹配,對?在這種情況下,你的班級模擬人員會說:「哦,那不是我期望的簽名」,並且試圖將它傳遞給解除訓練班,我相信。請參閱OCMock source中的forwardInvocationForClassObject。至於爲什麼你會得到NO(因爲底層類也返回YES,這使得這種測試類型沒有意義,但這是另一個問題),我不是100%確定,但也許它只是一個C主義 - 「不確定的值,空隙,0(假/否)」