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.
Related
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.
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];
i'm a new user of OCMock, so maybe i'm just missing something simple here. this code does not compile:
id mockSession = [OCMockObject mockForClass:[AVCaptureSession class]];
[[mockSession expect] addOutput:[OCMArg anyPointer]];
the error is
Multiple methods named 'addOutput:' found with mismatched result, parameter type or attributes
the signature of the method addOutput on AVCaptureSession is as follows
- (void)addOutput:(AVCaptureOutput *)output
as far as i can tell, the problem is that the method addOutput exists on both the AVCaptureSession and AVAssetReader classes. the method signature for addOutput on AVAssetReader is as follows.
- (void)addOutput:(AVAssetReaderOutput *)output
apparently the compiler thinks my mockSession is an AVAssetReader, but i don't know why it chooses that class instead of AVCaptureSession. if i expect a different method on AVCaptureSession that does not exist on AVAssetReader, then it compiles. i have tried the following without success. it compiles, but crashes.
id mockSession = [OCMockObject mockForClass:[AVCaptureSession class]];
[(AVCaptureSession*)[mockSession expect] addOutput:[OCMArg anyPointer]];
this code also does not compile, with the same error as the previous one
id mockSession = [OCMockObject mockForClass:[AVCaptureSession class]];
AVCaptureVideoDataOutput *output = [[AVCaptureVideoDataOutput alloc] init];
[[mockSession expect] addOutput:output];
any guidance here?
In cases where your variable is an "id" but a method is declared with different signatures in different classes you should help the compiler by casting the object to the correct type, e.g.
[((AVCaptureSession *)[mockSession expect]) addOutput:[OCMArg any]];
In either case, if the argument is an object, as it seems in your case, you should use any and not anyPointer. But you figured that one out already. ;-)
ok, i think it figured it out. as i suspected, it was a simple noob mistake. changing [OCMArg anyPointer] to [OCMArg any] makes the following work:
id mockSession = [OCMockObject mockForClass:[AVCaptureSession class]];
[(AVCaptureSession*)[mockSession expect] addOutput:[OCMArg any]];
You need to inform the compiler that it's fine
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-multiple-method-names"
#pragma clang diagnostic ignored "-Wstrict-selector-match"
OCMStub([globalContextMock sharedContext]).andReturn(context);
#pragma clang diagnostic pop