2012-03-07 103 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"]; 
} 

工作正常,我:)