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.
Related
Here is the testing code:
id dataControllerMock = [OCMockObject mockForClass:[RAMImsakyaDataController class]];
[[[dataControllerMock expect] andReturn:dataControllerMock] alloc];
(void)[[[dataControllerMock expect] andReturn:dataControllerMock] init];
[[[dataControllerMock stub] andReturn:#"30.06 , 50.67"] getLocationTitle];
[self.viewController viewDidLoad];
XCTAssertTrue([self.viewController.title isEqualToString:#"30.06 , 50.67"], #"View controller title is wrong");
[dataControllerMock verify];
The problem is that the dataControllerMock causing a failure with "unexpected method invoked: getLocationTitle"! I do stub the method. And even if I change the stub to expect, same thing. When I breakpoint inside viewDidLoad, the mock is already there as expected yet it doesn't recognize the getLocationTitle method.
Update: here is the viewDidLoad code
NSString *location = [self.dataController getLocationTitle];
if (location == nil) {
self.title = #"إمساكية رمضان ١٤٣٥ هـ";
} else {
self.title = [NSString stringWithFormat:#"إمساكية رمضان ١٤٣٥ هـ (توقيت %#)", location];
}
Why not take a different approach and use a partial mock?
RAMImsakyaDataController* realObject = [RAMImsakyaDataController new];
id partialObject = [OCMockObject partialMockForObject:realObject];
[[[partialObject stub] andReturn:#"30.06 , 50.67"] getLocationTitle];
[partialObject viewDidLoad]; // Method under test
XCTAssertTrue([partialObject.title isEqualToString:#"30.06 , 50.67"], #"View controller title is wrong");
I find that trying to mock alloc can lead to difficult behavior.
EDIT
// In MyViewController
- (RAMImsakyaDataController*)dataController {
if (!_dataController) {
_dataController = [[RAMImsakyaDataController alloc] init];
}
return _dataController;
}
Then partial mock the VC and replace this method with one that returns your partially mocked data controller.
My guess is that OCMock is failing to correctly mock the data controller's +alloc method so your view controller is using a real data controller instead of a mock.
I've had a difficult time mocking object creation. What I've ended up doing instead of trying to mock +alloc is putting testability stubs on the objects I want to test that create their dependencies, and then I can use a partial mock of the object under test to override the object creation. Like:
#implementation ViewController
- (void)init {
...
_dataController = [self newDataController];
...
}
- (DataController *)newDataController {
return [[DataController alloc] init];
}
#end
And then in my test
ViewController *underTest = [ViewController alloc];
id mockUnderTest = [OCMockObject partialMockForObject:underTest];
id mockDataController = [OCMockObject niceMockForClass:[DataController class]];
[[[mockUnderTest stub] andReturn:[mockDataController retain]] newDataController];
underTest = [underTest init];
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 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];