I have a note class with the following:
#interface Note
-(char)alphabetName;
#end
And I am trying to stub this out in a test:
id mockNote = [OCMockObject mockForClass:[Note class]];
[[[mockNote stub] andReturnValue:OCMOCK_VALUE((char){'A'})] alphabetName];
And my test suite will not run due to getting an error on the stub call: "Taking the address of a temporary object of type 'char'"
I can't reproduce this error. What happens if you get rid of the temporary?
id mockNote = [OCMockObject mockForClass:[Note class]];
char a = 'A';
[[[mockNote stub] andReturnValue:OCMOCK_VALUE(&a)] alphabetName];
Related
I am using OCMock on an objC project.
I have the following code:
DB_Account *Lena = [[DB_Account alloc] init];
Lena.niceName = #"Lena";
Lena.userId = #"Lena";
id mockStorageManager = OCMClassMock([V4_StorageManager class]);
[[[mockStorageManager stub] andReturn:Lena] getAccountByDBId:#1];
id mockDBNotificationManager = OCMClassMock([DBNotificationManager class]);
id partialV4DBNotificationManagerMock = OCMPartialMock([V4_DBNotificationManager manager]);
[[[mockDBNotificationManager stub] andReturn:(NotificationPolicy *)[NotificationPolicy Never]] getNotificationPolicy];
[[[partialV4DBNotificationManagerMock stub] andReturn:mockDBNotificationManager] dbNotificationManager];
BOOL shouldShow = [[V4_DBNotificationManager manager] showOnLoginExpired:Lena];
assertThatBool(shouldShow,is(isFalse()));
this code fails to compile on the following line:
[[[mockDBNotificationManager stub] andReturn:(NotificationPolicy *)[NotificationPolicy Never]] getNotificationPolicy];
with this error:
Error:(95, 5) multiple methods named 'getNotificationPolicy' found with mismatched result, parameter type or attributes
this method returns an object of type NotificationPolicy *, no other class implements or declares a method with this name.
What is wrong?
I had to do this to solve the problem
[(DBNotificationManager*)[[mockDBNotificationManager stub]
andReturn:[NotificationPolicy Never]] getNotificationPolicy];
I have a method I would like to test with OCMock but not sure how to do that. I need to mock
ExtClass which isn't defined as part of my code (external library):
+(NSString *)foo:(NSString *)param
{
ExtClass *ext = [[ExtClass alloc] initWithParam:param];
if ([ext someMethod])
return #"A";
else
return #"B";
}
Thanks in advance!
OCMock 2
id mock = [OCMockObject mockForClass:[ExtClass class]];
// We stub someMethod
BOOL returnedValue = YES;
[[[mock stub] andReturnValue:OCMOCK_VALUE(returnedValue)] someMethod];
// Here we stub the alloc class method **
[[[mock stub] andReturn:mock] alloc];
// And we stub initWithParam: passing the param we will pass to the method to test
NSString *param = #"someParam";
[[[mock stub] andReturn:mock] initWithParam:param];
// Here we call the method to test and we would do an assertion of its returned value...
[YourClassToTest foo:param];
OCMock3
// Parameter
NSURL *url = [NSURL URLWithString:#"http://testURL.com"];
// Set up the class to mock `alloc` and `init...`
id mockController = OCMClassMock([WebAuthViewController class]);
OCMStub([mockController alloc]).andReturn(mockController);
OCMStub([mockController initWithAuthenticationToken:OCMOCK_ANY authConfig:OCMOCK_ANY]).andReturn(mockController);
// Expect the method that needs to be called correctly
OCMExpect([mockController handleAuthResponseWithURL:url]);
// Call the method which does the work
[self.myClassInstance authStarted];
OCMVerifyAll(mockController);
Notes
Ensure that in both cases you stub two methods (alloc and the init... method). Also, make sure that both stubbing calls are made on the instance of the class mock (not the class itself).
Docs: Class methods section in the OCMock features
Alternatives
This (strange)solution may be useful in case you want to test legacy code that due to whatever reason you cannot refactor. However, if you can modify the code you should refactor it and get an ExtClass object as a parameter, not a string, delegating the creation of ExtClass out of that method. Your production and test code would be simpler and clearer, specially in a more complex real life case, not in this simple example.
So I have a class I wrote some test cases for. This class has these two methods:
- (void)showNextNewsItem {
self.xmlUrl = self.nextNewsUrl;
[self loadWebViewContent];
}
- (void)showPreviousNewsItem {
self.xmlUrl = self.previousNewsUrl;
[self loadWebViewContent];
}
Could be refactored and this is quite primitive, but nevertheless I just want to make sure next loads next and previous loads previous. So I use OCMock to instantiate a OCMockObject for my SUT class like this:
- (void)testShowNextOrPreviousItemShouldReloadWebView {
id mockSut = [OCMockObject mockForClass:[NewsItemDetailsViewController class]];
[[[mockSut expect] andReturn:#"http://www.someurl.com"] nextNewsUrl];
[[mockSut expect] loadWebViewContent];
[[[mockSut expect] andReturn:#"http://www.someurl.com"] previousNewsUrl];
[[mockSut expect] loadWebViewContent];
[mockSut showNextNewsItem];
[mockSut showPreviousNewsItem];
[mockSut verify];
}
The problem lies in the two lines actually calling the methods that do something to be verified. OCMock now tells me, that invoking showNextNewsItem and showPreviousNewsItem are not expected. Of course, it's not expected because I am in the test and I only expect certain things to happen in the production code itself.
Which part of the mocking concept didn't I understand properly?
It's generally confusing to mock the class under test, but if you want to do that, you need a "partial mock", so that you can call methods without stubbing them and have them execute the normal methods.
This appears to be supported in OCMock according to the docs.
I found the solution. Using a partialMock on the object does exactly what I want. This way, calls I explicitly define are mocked and I call the methods that are under test on the "not-mocked" object.
- (void)testShowNextOrPreviousItemShouldReloadWebView {
NewsItemDetailsViewController *sut = [[NewsItemDetailsViewController alloc] init];
id mockSut = [OCMockObject partialMockForObject:sut];
[[[mockSut expect] andReturn:#"http://www.someurl.com"] nextNewsUrl];
[[mockSut expect] loadWebViewContent];
[[[mockSut expect] andReturn:#"http://www.someurl.com"] previousNewsUrl];
[[mockSut expect] loadWebViewContent];
[sut showNextNewsItem];
[sut showPreviousNewsItem];
[mockSut verify];
}
I have some code I want to test that is passing around the address of a struct:
MyObject myObject = ...;
MyRecord record = [someObject record]; //record is a #property
[myObject add:&record];
I've mocked someObject to return a record:
MyRecord record = ...;
[[[_mockSomeObject stub] andReturnValue:OCMOCK_VALUE(record)] record];
[[_mockMyObject expect] add:&record];
Unfortunately, OCMock fails (I believe) because pulling the struct out of the NSValue wrapper will always return a different address.
Is there a way to get an expectation to work correctly if one of the parameters is an address of a struct?
Look at this post on returning structs with OCMock. Looks like the OCMOCK_VALUE macro just won't cut it.
I'm trying to mock a method that has the equivalent of the following signature:
- (NSDictionary *) uploadValues:(BOOL)doSomething error:(NSError **)error
I want it to return a small dictionary so that my test can make sure the code uses the dictionary properly. however, no matter what i do OCMock always returns nil from the method, regardless of how i stub it out. The error begins as nil in the code i'm testing, and these are the different ways i've tried stubbing it:
NSError * error = nil;
[[[mock stub] andReturn:someDict] uploadValues:YES error:&error];
[[[mock stub] andReturn:someDict] uploadValues:YES error:nil];
[[[mock stub] andReturn:someDict] uploadValues:YES error:[OCMArg any]];
and none of them work. Does OCMock support handles as stubbed message arguments and if so, what's the correct way to do it?
[[[mock stub] andReturn:someDict] uploadValues:YES error:[OCMArg setTo:nil]];
or
NSError* someError = ...
[[[mock stub] andReturn:someDict] uploadValues:YES error:[OCMArg setTo:someError]];
You could also do
[[[mock stub] andReturn:someDict] uploadValues:YES error:[OCMArg anyPointer]];
but this might cause your code to incorrectly think that you passed back a real NSError.
With ARC, your method declaration will probably look like this:
- (NSDictionary *) uploadValues:(BOOL)doSomething error:(NSError *__autoreleasing *)error;
Here is how I mock these types of methods:
BOOL mockDoSomething = YES;
NSError __autoreleasing *error = nil;
[[[mock stub] andReturn:someDict] uploadValues:OCMOCK_VALUE(mockDoSomething) error:&error];
NSError *__autoreleasing *err = (NSError *__autoreleasing *) [OCMArg anyPointer];
[[[_mockReporter stub] andReturnValue:OCMOCK_VALUE((BOOL){YES})]
yourMethodCall:err];
I created a category on OCMArg to aid with this situation.
OCMArg+Helpers.h:
#interface OCMArg (Helpers)
+ (NSError *__autoreleasing *)anyError;
#end
OCMArg+Helpers.m:
#implementation OCMArg (Helpers)
+ (NSError *__autoreleasing *)anyError {
return (NSError *__autoreleasing *)[OCMArg anyPointer];
}
#end
Then, whenever I have an error param I need to mock, use anyError, like so:
[[myMock stub] someMethodWithArg:anArg error:[OCMArg anyError]];
Sadly, I haven't found a good solution for this either. The best I can say is to try to make the use of the NSError** as small as possible, and then put it in an isolated function that you can completely mock out on your partial mock.
I'm finding that any code that uses anything other than an NSObject* (or derived) or primitive values (NSInteger, BOOL, etc) is pretty much impossible to test using OCMock.