OCMock failures "Another mock is already associated with object" with XCTest in XCode 7 - xctest

I just upgraded to XCode 7 recently, forcing me to upgrade OCMock so that I have support for x64 architectures. Apparently a change was made in OCMock which does not allow for a previously mocked object to be remocked-- in other words, I had a helper method that did something like this:
-(MyObject *)getObject {
Factory *factory = [self.dependencyInjector getInstance:factory];
id mockFactory = [OCMockObject partialMockForObject:factory];
[[[mockFactory stub] andReturn:#"important-value"] thing];
return [[MYObject alloc] initWithFactory:mockFactory];
}
This worked fine previously, but apparently there was a change to OCMockObject to not allow a re-mocking of an already mocked object. Since the factory object returned by the injector is effectively a singleton, subsequent calls to the getObject method is calling partialMockForObject: on it multiple times and this now throws an exception "Another mock is already associated with object".
Is there any way to make OCMock not throw an error? I tried calling stopMocking on the object prior to mocking it, but that does not fix this issue. The only way around it was to do something like:
-(MyObject *)getObject {
if (!self.mockFactory) {
Factory *factory = [self.dependencyInjector getInstance:factory];
id mockFactory = [OCMockObject partialMockForObject:factory];
[[[mockFactory stub] andReturn:#"important-value"] thing];
self.mockFactory = mockFactory;
}
return [[MYObject alloc] initWithFactory:self.mockFactory];
}
which is really annoying to have to do...

Looking at the code as it is today, the implementation of stopMocking explicitly resets the associated object (https://github.com/erikdoe/ocmock/blob/dd5599695dcc50afe4d6bdff509ed3cbe389c667/Source/OCMock/OCPartialMockObject.m#L80). I'm at a loss how calling stopMocking doesn't solve the problem. Can you build a debug version of OCMock and set a breakpoint at the line highlighted above and see whether it's called?

Related

How to partially mock an object inside legacy code with OCMock?

I would like to accomplish what also is described here, i.e create mocks inside legacy code. However I require partial instead of nice or strict mocks.
For example, consider leaderboards that behave exactly like GKLeaderbaord except for implementing a stubbed version of loadScoresWithCompletionHandler:.
I've tried this code inside an XCTestCase but it currently fails at runtime in the indicated line: OCMInvocationMatcher raises an EXC_BAD_ACCESS error. Perhaps there is some infinite recursion going on.
id leaderboardMock = OCMClassMock(GKLeaderboard.class);
OCMStub([leaderboardMock alloc])
.andReturn(OCMPartialMock([GKLeaderboard alloc]));
OCMStub([leaderboardMock loadScoresWithCompletionHandler: [OCMArg any]])
.andDo(^(NSInvocation *invocation) { /* ... */ });
// these parts normally nested inside legacy code
GKLeaderboard *leaderboard = /* raises EXC_BAD_ACCESS */
[[GKLeaderboard alloc] initWithPlayers: #[ GKLocalPlayer.localPlayer ]];
leaderboard.identifier = #"Test";
[leaderboard loadScoresWithCompletionHandler: nil /* ... */ ];
What am I doing wrong and is this even possible for partial mockups?
UPDATE I can by now see how the indicated line might (quite obviously) cause an infinite recursion, but don't yet know how to avoid (or break) it.
UPDATE I've also had no success with an attempt of bringing in an dedicated class with OCMStub([leaderboardMock alloc]).andReturn([LeaderboardMock alloc]) (nor with OCMStub([leaderboardMock initWithPlayers: [OCMArg any]]).andReturn([[LeaderboardMock alloc] initWithPlayers:nil])). Perhaps OCMock does its magic at the level of init (the documentation says: "it is not possible to stub the init method, because that is implemented by the mock itself") hence such an attempt the level of alloc (or initWithPlayers:) cannot have its desired effect.
Not sure I follow what you are trying to do. It seems like a misunderstanding. Does the following not work for you?
GKLeaderboard *leaderboard = [[GKLeaderboard alloc] initWithPlayers: ... ];
id leaderboardMock = OCMPartialMock(leaderboard);
OCMStub([leaderboarMock loadScoresWithCompletionHandler: ...]);
You can use the normal object without restrictions. You can use the partial mock created for the object to manipulate the actual instance in leaderboard. That's the beauty of partial mocks.
UPDATE: If the object creation is not under your control, you can try the following:
GKLeaderboard *leaderboard = [[GKLeaderboard alloc] initWithPlayers: ... ];
id leaderboardMock = OCMPartialMock(leaderboard);
OCMStub([leaderboardMock alloc]).andReturn(leaderboardMock);
OCMStub([leaderboardMock initWithPlayers:[OCMArg any]).andReturn(leaderboard);
OCMStub([leaderboarMock loadScoresWithCompletionHandler: ...]);
I have by now concluded that method swizzling would be a possible choice.
A replacement method could e.g. generate a partial mockup from within the context of legacy code and hence introduce a partial mock in that context without requiring changes to legacy APIs.
you should not use following line, it will mock your entire class and none of real object will get called.
OCMClassMock(GKLeaderboard.class)

How can I unit-test init methods in Objective-C?

I'm unit testing a Mac App in Xcode 5 using the new XCTest framework. Specifically, I'm trying to test that a class implements an (as yet unwritten!) - (id)initWithStream:(NSInputStream)stream method. So I have the start of my test:
- (void)testInitWithStream
{
// Should have an init method named initWithStream:
XCTAssertTrue([[MYParser alloc]
respondsToSelector:#selector(initWithStream:)],
#"'initWithStream:' not implemented.");
Which works fine; the test currently fails as expected. It's the next part, which attempts to run the as-yet-unwritten method, that's causing me grief:
// Should return nil if no valid stream is passed
XCTAssertNil([[MYParser alloc]
performSelector:#selector(initWithStream:)
withObject:nil],
#"should get nil on 'initWithStream:' with no stream.");
Xcode gives me the error "PerformSelector names a selector which retains the object." I'm using performSelector:withObject: because attempting to call the selector directly results in an error that "No visible #interface for 'MYParser' declares the selector 'initWithStream:'."
My test suite has GCC_WARN_UNDECLARED_SELECTOR turned off, but these are errors, not warnings. How can I test the init methods of objects when the test needs to cover the situation where the method may not be implemented (even as a definition) yet?
The method may not be implemented yet because I'm writing my tests first; is this right, or should I be writing my class definitions first, then my tests, then the actual implementation? (I'm looking for consensus and best-practice on this point, not just opinion.)
If you are doing test first development, you don't need to test for respondsToSelector:#selector(initWithStream:). Directly calling initWithStream: in your test suite will at first not compile. This is your failing test that you should fix before writing new code. How do you fix it? By implementing the method:
- (instancetype)initWithStream:(id)stream {
return [super init];
}
Your test now compiles, which is a little bit better than before. Running the test will fail though, because obviously the implementation doesn't do what you're testing for. Now write more code to make the test actually pass:
- (instancetype)initWithStream:(id)stream {
if (!stream) {
return nil;
}
return [super init];
}
Next, you can test for stream not being nil, which will fail so you write more code to fix it.

Unit testing with OCMock and MagicalRecord

Although I have seen similar questions in SO, none of the answers seems to solve my problem.
I have a NSManagedObject class generated by mogenerator with custom functions (not in the model):
#interface MyManagedClass : _MyManagedClass {
-(NSNumber*)getRandomNumber2;
-(void)function_I_want_to_test;
}
My function_I_want_to_test() depend on the result of random(), and that is something I must control during testing. Since I cannot mock random() I built a function wrapper, which by the way, is not static because I had many problems with OCMock and static class functions.
The setup of my unit test looks like:
[MagicalRecord setDefaultModelFromClass:[self class]];
[MagicalRecord setupCoreDataStackWithInMemoryStore];
Using the debugger I could verify that the model is properly loaded. Also if I do it the non magical way:
NSBundle *b = [NSBundle bundleForClass:[self class]];
model = [NSManagedObjectModel mergedModelFromBundles:#[b]];
After this point I cannot create any mock to stub my random() wrapper function
I have tried a class mock
id mock = [OCMockObject mockForClass:[MyManagedClass class]];
[[[mock stub] andReturn:#50] getRandomNumber2];
MyManagedClass *my_object = [mock MR_createEntity];
I have tried using a partial mock
MyManagedClass *my_object = [MyManagedClass MR_createEntity];
id mock2 = [OCMockObject partialMockForObject:my_object];
After last point, just creating an instance of mock2 destroys the dynamic properties of my_object, and it become useless.
I have also tried using a mock protocol with the function I want to stub, still to no avail.
The runtime exception is the normal one other people get when using with tests with Core Data objects: the properties are not recognized selectors.
However the strange thing to me is that I am not trying to stub any dynamic property, but a normal, compile time known function. Hence it seems strange to me that using OCMock renders my instances useless.
Ideally I would like something that uses OCMock/Mogenerator/Magicalrecord.
what am I doing wrong?
I recommend against trying to mock out managed objects. There's a lot of runtime craziness going on to make managed objects work. That's why I suggest going with an in memory database approach for testing. This will let you create empty instances of your entities while letting the core data stuff happen.
Since you're probably using unit tests, I suggest that in each test case where you think you need to mock out some data to instead recreate your whole stack, and set a new entity up with the state it needs to run your test. You can also make a test in memory persistent store separate from the one the default stack create method gives you, and attach this second one to your default stack. That is, make a new in memory store, initialize it with your fake/mock data entities, and attach that to your test data stack.
I hope this rambling helps a bit, but the bottom line is dont mock managed objects...really.
You could do it by moving your random number generation out of the Core Data object and into a helper class:
#implementation RandomNumberGenerator
static RandomNumberGenerator *_sharedInstance = nil;
+(RandomNumberGenerator *)sharedInstance {
if (_sharedInstance == nil) {
_sharedInstance = [[RandomNumberGenerator alloc] init];
}
return _sharedInstance;
}
+(void)setSharedInstance:(RandomNumberGenerator *)instance {
[instance retain];
[_sharedInstance release];
_sharedInstance = instance;
}
-(NSNumber *)generateRandomNumber {
return ...
}
#end
Then in your test:
-(void)testSomething {
id mockGenerator = [OCMockObject mockForClass:[RandomNumberGenerator class]];
[RandomNumberGenerator setSharedInstance:mockGenerator];
[[[mockGenerator stub] andReturn:#(4)] generateRandomNumber];
MyManagedClass *my_object = [MyManagedClass MR_createEntity];
expect(my_object.someField).to.equal(someValueBasedOnGeneratedRandomNumber);
}

Objective C - OCMock and stubbing?

Is it possible to have an actual object of a class and only mock a method in that class instead of mocking the whole object?
I want the object to behave 100% the same as the real object except 1 method.
Ex:
MyObject *object = [[MyObject alloc] init];
[[[object stub] andReturn:#"some_string"] getMyString];
Yes, that's what partial mocks are for.
Partial mocks
id aMock = [OCMockObject partialMockForObject:anObject]
Creates a mock object that can be used in the same way as anObject. When a method that is not stubbed is invoked it will be forwarded to anObject. When a stubbed method is invoked using a reference to anObject, rather than the mock, it will still be handled by the mock.
Note that currently partial mocks cannot be created for instances of toll-free bridged classes, e.g. NSString.
See http://www.mulle-kybernetik.com/software/OCMock/
You can see on documentation:
10.3 Partial mocks cannot be created for certain special classes

Obj-C using #selector on a static method from a static method in the same class?

I have two static methods/selectors in the same class, one passes the other as a callback to an external method. However, how I have it coded I get an error. This worked when both the methods were instance methods, and I've read it can work when the first method is an instance method using [self class]. However, I haven't found information when both are static, and I haven't got it to work.
+(void)Validate {
Callback *managerCallback = [[[Callback alloc] initWithTarget:self Action:#selector(Parse:)] autorelease];
...
}
+(void)Parse:(Callback *)managerCallback {
...
}
Thanks!
Callback *managerCallback = [[[Callback alloc] initWithTarget:self Action:#selector(Parse:)] autorelease];
That line of code is setup to call the instance method Parse:, not a class method as you have it defined.
Objective-C does not have static methods. It has class methods and instance methods.
As well, your methods should start with lowercase letters.
Herp-da-derp. Dave is right.
Given this:
+(void)Validate {
Callback *managerCallback = [[[Callback alloc] initWithTarget:self Action:#selector(Parse:)] autorelease];
...
}
+(void)Parse:(Callback *)managerCallback {
...
}
Some comments:
methods should start with lowercase letters
it is exceedingly odd to use a class in such a role; even if you really only ever need one of 'em, use an instance. At the least, the instance is a convenient bucket to toss state in and it'll make refactoring in the future much easier if you ever need two.
The above pattern makes the assumption (and I ASSumed) that the instance of Callback is retained. For callbacks, timers, and some other patterns, this is typical; retain the target until the target is called for the last time. Then release (or autorelease). However, notification centers do not do this. Nor are delegates retained, typically.
Turns out the code is written correctly to do what I wanted to, but because callback was set to autorelease, the object was getting released before the callback was being processed.