用OCMock嘲笑KVO

Dov*_*Dov 4 cocoa unit-testing objective-c key-value-observing ocmock

我想测试Key-Value-Observation是否适用于我的一类.它有一个属性,这取决于另一个属性.它们的设置如下:

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

- (NSArray *)secondProperty {
    return [self.firstProperty array];
}
Run Code Online (Sandbox Code Playgroud)

我想运行一个单元测试来验证firstProperty更改时,绑定的对象是否secondProperty获得通知.起初我以为我可以使用+[OCMockObject observerMock],但它看起来只能用于NSNotificationCenter.测试这个的最佳方法是什么?

Dov*_*Dov 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];
Run Code Online (Sandbox Code Playgroud)

当我运行此测试代码时,我收到以下消息:

OCMockObject[NSObject]: unexpected method invoked: isKindOfClass:<??> 
    expected:     observeValueForKeyPath:@"firstProperty" ofObject:
Run Code Online (Sandbox Code Playgroud)

我做了一些调查,发现-isKindOfClass:模拟对象没有想到的调用是传递给一个NSKeyValueObservance类对象.

我尝试添加以下代码来模拟响应,但是YES并且NO两者的值都失败EXC_BAD_ACCESS,并且堆栈中存在NSKeyValueWillChange的异常.

BOOL returnVal = NO;
[[[secondPropertyObserver stub] andReturnValue:OCMOCK_VALUE(returnVal)] isKindOfClass:[OCMArg any]];
Run Code Online (Sandbox Code Playgroud)

我更谨慎地走了一步,发现我的代码没有导致这个异常 - 它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"];
Run Code Online (Sandbox Code Playgroud)