I'm trying to set up a simple OCMock unit test in an iOS project, just to familiarize myself with the framework.
I have a mocked DataLoader
class, and even though I'm calling the method myself, my expectation fails:
- (void)testSimpleMocking {
// Mock the class
id mock = [OCMockObject niceMockForClass:[DataLoader class]];
// Override the 'dispatchLoadToAppDelegate:' to be a no-op
[[[mock stub] andReturn:nil] dispatchLoadToAppDelegate:[OCMArg any]];
// Expect the method to be called
[[mock expect] dispatchLoadToAppDelegate:[OCMArg any]];
// Call the method
[mock dispatchLoadToAppDelegate:nil];
// Verify
[mock verify];
}
However, when I run this test, I receive the error:
/Users/Craig/projects/MyApp/Unknown.m: -[MockingDataLoaderTest testSimpleMocking] : OCMockObject[DataLoader]:
expected method was not invoked: dispatchLoadToAppDelegate:<OCMAnyConstraint: 0x1a3d890>
How is this possible, when I am calling the method myself?
Edit: A more complex case:
- (void)testDataLoaderWaitsForDownload {
id mock = [OCMockObject niceMockForClass:[DataLoader class]];
id metadataItem = [OCMockObject niceMockForClass:[NSMetadataItem class]];
// Prepare NSMetadataItem
[[[metadataItem expect] andReturn:nil] valueForAttribute:NSMetadataItemURLKey];
// CODERUN
[mock waitForDownload:metadataItem thenLoad:YES];
//VERIFY
[metadataItem verify];
}
And the implementation of the waitForDownload:thenLoad:
method:
- (void)waitForDownload:(NSMetadataItem *)file thenLoad:(BOOL)load {
NSURL *metadataItemURL = [file valueForAttribute:NSMetadataItemURLKey];
...
Fails with the error:
Unknown.m:0: error: -[MockingDataLoaderTest testDataLoaderWaitsForDownload] : OCMockObject[NSMetadataItem]: expected method was not invoked: valueForAttribute:@"kMDItemURL"
In your test, stub
is taking priority because it was called first. If you switch the order of your expect
and stub
your test should pass.
The reason you would use both expect
and stub
together (with the same argument expectations) is to ensure that at least one call occurs, but then to respond to subsequent calls without failure.
If you truly looking for just one call to a method, just add the andReturn:
to the expect clause...
- (void)test_dispatchLoadToAppDelegate_isCalledExactlyOnce {
// Mock the class
id mock = [OCMockObject niceMockForClass:[DataLoader class]];
// Expect the method to be called
[[[mock expect] andReturn:nil] dispatchLoadToAppDelegate:[OCMArg any]];
// Call the method
[mock dispatchLoadToAppDelegate:nil];
// Verify
[mock verify];
}
An alternate scenario:
- (void)test_dispatchLoadToAppDelegate_isCalledAtLeastOnce {
// Mock the class
id mock = [OCMockObject niceMockForClass:[DataLoader class]];
// Expect the method to be called
[[[mock expect] andReturn:nil] dispatchLoadToAppDelegate:[OCMArg any]];
// Handle subsequent calls
[[[mock stub] andReturn:nil] dispatchLoadToAppDelegate:[OCMArg any]];
// Call the method
[mock dispatchLoadToAppDelegate:nil];
// Call the method (again for fun!)
[mock dispatchLoadToAppDelegate:nil];
// Verify
[mock verify];
}
For this particular case, it looks like you could use niceMockForClass
but if you wanted the stub to return a non-nil, then you'd have to call stub
either way.
Ben Flynn is correct that reversing the order of stub
and expect
should make your test pass, but I'd go a step further and suggest you should remove the stub
call. The way you've written this test suggests that stub
is a prerequisite to expect
, which it's not.
expect
means the method must be invoked once and only once (for each expectation). stub
means the method may be called zero or more times. Generally you expect
calls that are important to the test, and stub
things that are side effects. Or use a nice mock and only set expectations for the important calls.
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