2012-03-07 106 views
4

我想測試關鍵值觀察對於我的一個類是否正常工作。它有一個屬性,這取決於另一個屬性。他們建立像這樣:用OCMock嘲笑KVO

+ (NSSet *)keyPathsForValuesAffectingSecondProperty { 
    return [NSSet setWithObjects: 
      @"firstProperty", 
      nil]; 
} 

- (NSArray *)secondProperty { 
    return [self.firstProperty array]; 
} 

我想運行一個單元測試,以驗證時firstProperty變化,勢必secondProperty對象得到通知。起初我以爲我可以使用+[OCMockObject observerMock],但它看起來像只能用於NSNotificationCenter。什麼是測試這個最好的方法?

回答

7

我曾在這一段時間@ chrispix的回答後,激發了我在不同的方向去努力。我開始與此:

id objectToObserve = [[TheClassBeingTested alloc] init]; 

id secondPropertyObserver = [OCMockObject mockForClass:[NSObject class]]; 
[[secondPropertyObserver expect] observeValueForKeyPath:@"secondProperty" 
               ofObject:objectToObserve 
               change:OCMOCK_ANY 
               context:[OCMArg anyPointer]]; 
[objectToObserve addObserver:secondPropertyObserver 
        forKeyPath:@"secondProperty" 
        options:NSKeyValueObservingOptionNew 
        context:NULL]; 

// Do something to modify objectToObserve's firstProperty  

[secondPropertyObserver verify]; 

當我運行這個測試代碼,我得到了以下信息:

OCMockObject[NSObject]: unexpected method invoked: isKindOfClass:<??> 
    expected:  observeValueForKeyPath:@"firstProperty" ofObject: 

我做了一些調查,發現-isKindOfClass:調用模擬對象沒想到的是正在通過一個NSKeyValueObservance類對象。

我嘗試添加以下代碼來模擬的響應,但YESNO兩個值失敗,並在堆EXC_BAD_ACCESS例外與NSKeyValueWillChange。

BOOL returnVal = NO; 
[[[secondPropertyObserver stub] andReturnValue:OCMOCK_VALUE(returnVal)] isKindOfClass:[OCMArg any]]; 

我走到更仔細,發現我的代碼並沒有造成這種例外 - 它同時autoreleasepool正在耗盡。然後,我明白我需要刪除觀察者。以下是完整的解決方案,包括刪除觀察者。

id objectToObserve = [[TheClassBeingTested alloc] init]; 

id secondPropertyObserver = [OCMockObject mockForClass:[NSObject class]]; 

BOOL returnVal = NO; 
[[[secondPropertyObserver stub] andReturnValue:OCMOCK_VALUE(returnVal)] isKindOfClass:[OCMArg any]]; 

[[secondPropertyObserver expect] observeValueForKeyPath:@"secondProperty" 
               ofObject:objectToObserve 
               change:OCMOCK_ANY 
               context:[OCMArg anyPointer]]; 

[objectToObserve addObserver:secondPropertyObserver 
        forKeyPath:@"secondProperty" 
        options:NSKeyValueObservingOptionNew 
        context:NULL]; 

// Do something to modify objectToObserve's firstProperty  

[secondPropertyObserver verify]; 

[objectToObserve removeObserver:secondPropertyObserver 
        forKeyPath:@"secondProperty"]; 
2

如果有一些結果secondProperty收到您可以驗證的通知,我只是將其設置爲一個實際的對象,然後驗證狀態。如果這是不可能的,你可以創建的secondProperty部分模擬,並期望KVO回調:

id observedObject = // ...initialize observed class 
observedObject.secondProperty = // initialize secondProperty 
id mockSecondProperty = [OCMockObject partialMockForObject:observedObject.secondProperty]; 
[[mockSecondProperty expect] observeValueForKeyPath:@"firstProperty" ofObject:observedObject change:OCMOCK_ANY context:OCMOCK_ANY]; 

observedObject.firstProperty = // modify firstProperty 

[mockSecondProperty verify]; 
1

只要對此感興趣的人:這是一個沒有OCMock的解決方案。

我只是將我希望改變的值綁定到單元測試類的測試屬性。

在下面的示例中,我希望numberOfPages屬性得到更新,並在更改pages陣列時激發KVO通知。

我宣佈我的DocumentTestsboundInteger屬性:

@interface DocumentTests : SenTestCase 
@property (assign) int boundInteger; 
@end 

然後我實現了我的測試情況下,像這樣:

- (void)testNumberOfPages 
{ 
    Document *doc = [[Document alloc] init]; 

    [self bind:@"boundInteger" toObject:doc withKeyPath:@"numberOfPages" options:nil]; 

    doc.pages = arrayOfThreePagesIHaveBuildSomewhereElse; 

    STAssertEquals(self.boundInteger, 3, @"Wrong number of pages."); 
    STAssertEquals(doc.numberOfPages, 3, @"Wrong number of pages."); 

    [self unbind:@"boundInteger"]; 
} 

工作正常,我:)