I would like to test that Key-Value-Observation is working properly for a class of mine. It has one property, which depends on another. They are set up like so:
+ (NSSet *)keyPathsForValuesAffectingSecondProperty {
return [NSSet setWithObjects:
@"firstProperty",
nil];
}
- (NSArray *)secondProperty {
return [self.firstProperty array];
}
I want to run a unit test to verify that when firstProperty
changes, an object bound to secondProperty
gets a notification. At first I thought I would be able to use +[OCMockObject observerMock]
, but it looks like that can only be used with NSNotificationCenter
. What would be the best way to test this?
I worked on this for a while after @chrispix's answer inspired me to work in a different direction. I started with this:
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];
When I ran this test code, I got the following message:
OCMockObject[NSObject]: unexpected method invoked: isKindOfClass:<??>
expected: observeValueForKeyPath:@"firstProperty" ofObject:
I did some investigation and found that the -isKindOfClass:
call the mock object didn't expect was being passed an NSKeyValueObservance
class object.
I tried adding the following code to mock a response, but values of YES
and NO
both fail with EXC_BAD_ACCESS
exceptions with NSKeyValueWillChange in the stack.
BOOL returnVal = NO;
[[[secondPropertyObserver stub] andReturnValue:OCMOCK_VALUE(returnVal)] isKindOfClass:[OCMArg any]];
I stepped more carefully and found that my code wasn't causing this exception - it was while the autoreleasepool
was being drained. It then dawned on me that I needed to remove the observer. Below is the complete solution, including removing the observer.
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"];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With