This is an extremely basic question about OCMock expectations. Let's you have an instance method methodA on objectA that calls an instance method methodB on objectA.
- (void)methodA {
[self methodB];
}
- (void)methodB {
...
}
Now, let's say I want to verify that a partial mock of objectA invokes methodA and methodB. It seems like all you should have to do is:
- (void)test {
id mockObjectA = [OCMockObject partialMockForObject:self.objectA];
[[mockObjectA expect] methodA];
[[mockObjectA expect] methodB];
[self.objectA methodA];
[mockObjectA verify];
}
When I run the test, it thinks that methodA was successfully invoked but that methodB was not successfully invoked. What am I misunderstanding?
Thanks for the help.
You need to tell the mock to forward the method call on to the real object so the implementation will actually run.
[[[mockObjectA expect] andForwardToRealObject] methodA];
Related
I have the following method:
- (void) someMethod
{
if ([super respondsToSelector:#selector(someMethod)])
{
[super performSelector:#selector(someMethod)
withObject:nil];
}
}
someMethod does not exist on superclass. as i understand, if there is no such method, runtime will ask the next responder in chain for such method till the NSObject class. And i was sure, that if statement will return NO.
Statement return YES. After that it performs selector without crash. As result - infinite recursion.
so, i have two questions:
Why [super respondsToSelector:#selector(someMethod)] returns YES ?
Why [super performSelector:#selector(someMethod) withObject:nil] does not crash with error 'does not responds to selector' ?
I think i've missed something essential.
Please, help.
Yes, you missed something essential as you suggest. From the documentation for respondsToSelector:
You cannot test whether an object inherits a method from its superclass by sending respondsToSelector: to the object using the super keyword. This method will still be testing the object as a whole, not just the superclass’s implementation. Therefore, sending respondsToSelector: to super is equivalent to sending it to self. Instead, you must invoke the NSObject class method instancesRespondToSelector: directly on the object’s superclass, as illustrated in the following code fragment.
if( [MySuperclass instancesRespondToSelector:#selector(aMethod)] )
{
// invoke the inherited method
[super aMethod];
}
HTH
Consider the following code
OCMockObject *mock = [OCMockObject mockForClass:[NSObject class]];
[[[mock expect] andReturnValue:OCMOCK_VALUE((BOOL){YES})] isEqual:[OCMArg any]];
[mock isEqual:[NSObject new]];
[mock verify];
Can someone, please, tell me why this fails with
test failure: -[NSObject_tests testIsEqualIsCalled] failed:
OCMockObject[NSObject]: expected method was not invoked: (null)
This makes literally no sense. I tried using andReturn: instead of andReturnValue: and still nothing.
expect returns an instance of OCMockRecorder. OCMock works by intercepting methods that OCMockRecorder does not implement. So you can't really mock methods defined by NSProxy, NSObject, or OCMockRecorder. You'll get the same results with this test:
-(void)testSomething {
id mock = [OCMockObject mockForClass:[NSObject class]];
[[[mock expect] andReturn:OCMOCK_VALUE((int){2})] hash];
[mock hash];
[mock verify];
}
If this is more than an academic exercise and you want to actually test a custom isEqual: implementation, there should be no reason to mock it. Just call isEqual: directly with objects that should and shouldn't match in your test.
I'm new to OCMock, so I may have overlooked something simple, but my issue is that I can't seem to stub the method class on a mock I've created. Here's how I'm setting up part of my test:
// Unit Test
id mock = [OCMockObject mockForClass:[MySubClass class]];
[[[mock stub] andReturn:[MySubClass class]] class];
...
[someObject someMethodWithParam:mock];
...
Here's my implementation of someMethodWithParam::
// Implementation
- (void)someMethodWithParam:(MySuperClass *)param {
[[param class] someClassMethod];
}
The problem is that [param class] returns OCClassMockObject instead of MySubClass. This results in an "unrecognized selector sent to class" error when someClassMethod is called. I've tried using a partial mock, but that didn't seem to help.
Edit:
Here's a simplified test that won't pass:
// Unit Test
id mock = [OCMockObject mockForClass:[MySubClass class]];
[[[mock stub] andReturn:[MySubClass class]] class];
XCTAssertEqual([mock class], [MySubClass class], #"The mock's class should be MySubClass");
Look at your -someMethodWithParam:. It not return anything. It's void. So this is wrong test for this method. You made mock which have to return something even if method can't return anything.
This test should looks like following:
MyClass object = [MyClass new]; /// instance of class which has someMethodWithParam: method
id mock = [OCMockObject partialMockForObject:object];
[[mock expect] someClassMethod];
[mock someMethodWithParam:[OCMArg any]];
[mock verify];
In this method only what you can is test expectation. You can test if method in your method has been called. So you can test if when you call -someMethodWithParam method -someClassMethod has been called. That's all. And you have to create partial mock.
I'm trying to deal with OCMock. I created simple class MyClass.
#interface MyClass : NSObject
- (NSString *)simpleMethod;
#end
#implementation MyClass
- (NSString *)simpleMethod {
[self method];
return #"simple";
}
- (void)method {
NSLog(#"ABC");
}
#end
What I want to check is if method method was invoked when simpleMethod has been called. Now I've got following code but it doesn't work:
- (void)testMethodInvoked
{
id mock = [OCMockObject mockForClass:[MyClass class]];
[[mock stub] simpleMethod];
SEL selector = NSSelectorFromString(#"method");
[[mock expect] methodForSelector:selector];
[mock verify];
}
How should I test this case? I think that is pretty easy to do, but I have no idea how solve this problem.
How to create mock and call method simpleMethod which invoke method method?
Current log:
<unknown>:0: error: -[OCMockTestTests testOne] : OCMockObject[MyClass]: expected method was not invoked: methodForSelector:#selector(method)
You never actually create an object of the class that you want to test. Also, you have to expect first, then invoke the method:
- (void)testMethodInvoked
{
// first create an object that you want to test:
MyClass *object = [[MyClass alloc] init];
// create a partial mock for that object
id mock = [OCMockObject partialMockForObject:object];
// tell the mock object what you expect
[[mock expect] method];
// call the actual method on the mock object
[mock simpleMethod];
// and finally verify
[mock verify];
}
I sometimes find it useful to test "private" methods / implementations -- perhaps don't call it a unit test if that breaks some kind of orthodoxy -- but for a complex implementation I may want to verify behavior on a more granular level than through the external interface.
In any event, I will expose class extension methods by creating a category in the test class:
#interface MyClass (ExposeForTest)
- (void)method;
#end
- (void)testMyClass
{
id mock = [OCMockObject mockForClass:MyClass.class];
[[mock expect] method];
[mock simpleMethod];
}
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.