OCMock: mocked protocol isn't stopped correctly - ios

I have a test case using OCMock which does the following:
CAAOAuth2AuthenticationManager *oAuth2AuthManager = [[CAAOAuth2AuthenticationManager alloc] init];
id authDelegate = [OCMockObject mockForProtocol:#protocol(CAAAuthenticationDelegate)];
id partialAuthManagerMock = [OCMockObject partialMockForObject:oAuth2AuthManager];
id resultMock = [OCMockObject mockForClass:[CAAOAuth2AuthenticationResult class]];
[[authDelegate reject] didFailWithError:OCMOCK_ANY];
[[[partialAuthManagerMock expect] andForwardToRealObject] authenticateWithResult:OCMOCK_ANY formData:OCMOCK_ANY delegate:authDelegate];
[[partialAuthManagerMock reject] authenticateWithOptions:OCMOCK_ANY delegate:authDelegate];
[[[resultMock expect] andReturnValue:OCMOCK_VALUE(YES) ] isAuthenticated];
[[resultMock reject] refreshToken];
When I run the test cases, a second test case (completely different test class and file) which also uses the CAAAuthenticationDelegate protocol fails with SIGABRT:
2014-02-28 10:11:24.594 otest[37161:303] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'OCMockObject[CAAAuthenticationDelegate]: unexpected method invoked: didReceiveAuthenticationWithResult:OCMockObject[CAAOAuth2AuthenticationResult]
stubbed: didFailWithError:<OCMAnyConstraint: 0xa913fc0>'
But, I don't use any mocks in the second test case. I was trying to clear the mocks with stopMocking with no success.
The following mock setup works without any problems:
[[authDelegate reject] didFailWithError:OCMOCK_ANY];
[[[partialAuthManagerMock expect] andForwardToRealObject] authenticateWithResult:OCMOCK_ANY formData:OCMOCK_ANY delegate:authDelegate];
[[partialAuthManagerMock expect] authenticateWithOptions:OCMOCK_ANY delegate:authDelegate];
[[[resultMock expect] andReturnValue:OCMOCK_VALUE(NO) ] isAuthenticated];
[[[resultMock expect] andReturn:refreshToken] refreshToken];
Can someone tell me, why this happens?

As a workaround, can you create an empty implementation of the protocol and then mock a real object? I've had better luck with that method -- mocking protocols has only led to wonkiness for me.
#interface TestAuthDelegateImpl : NSObject <CAAAuthenticationDelegate>
#end
#implementation
- (void)didFailWithError:(id)whatever;
#end
Something like that. Then just mockForClass it -- might be better behaved.

That seems to mean that your CAAOAuth2AuthenticationManager instance was still around in a later test, and still had the old mock delegate set on it, and some method was called on it which caused that delegate method to be called. Is CAAOAuth2AuthenticationManager a singleton-type object, or is the same instance used in the second test? I would reset the delegate to nil on the auth manager in the first test when it is done.
You can also use niceMockForProtocol, which will silently ignore any method calls which do not have an explicit reject set up on them. By the exception, it sounds like the reject has been removed, and the delegate mock will now just throw exceptions on any method sent to it, since there are also no expects set up.
Also, I would use STAssertNoThrow() around the actual call to your real code (which presumably happens after the setup you show above). Rejects and unexpected methods will raise exceptions, which can cause the mock objects to not get deallocated properly and create issues for subsequent tests. If the test in question passed though, that is probably not an issue here.
The last thing to check is if your delegate property is declared as "assign" instead of "weak". If it's "assign", and you don't nil it out and it gets freed, then anything could happen (segfault, or an entirely new object getting allocated at that same memory address). That also seems unlikely here though.

Related

How to prevent EXC_BAD_ACCESS on an object I don't know is released in iOS 9?

I use an NSURLConnection to download a file and when the user taps back I don't necessarily know if the connection is finished and has been properly disposed. So I added the following check where if the connection is not null, cancel it.
if (self.urlConnection){
[self.urlConnection cancel];
}
This worked in iOS 7/8 and I never once received an exception. But now in iOS 9 when I do the check to see if the connection exists (and it doesn't) I get an exception. This is the first line above, before I have actually made a call to the url.
I don't understand why checking to see if an object is nil would ever throw an exception and if it does - how can I be expected to guard against this exception.
Is there a new way to make sure an object exists and hasn't been released before I make a call to it?
Edit: This is how the property is declared:
#property (nonatomic, assign) NSURLConnection *urlConnection;
If the object hasn't actually been instantiated yet, the check works fine. It's only when the connection finishes and becomes nil and then I try to check if it is nil that the check explodes. This wasn't happening on earlier iOS versions.
Yes, there is ... use a weak reference.
__weak typeof(self) weakSelf = self;
...
..
.
[weakSelf.urlConnection cancel]
NSURLConnection is an object and needs to be a strong reference. Assign is only appropriate for primitive values. If you really intended to make this property weak, then use this pattern to ensure that your object does not go out of scope while using it:
//code that goes in some method
NSURLConnection* strongConnection = self.urlConnection;
if(strongConnection) {
//code to do stuff with the url connection
[strongConnection cancel];
}
I also urge caution with weak references particularly when you have said object handling notifications or observing a change. I've been bitten more than once with the weak object getting destroyed and the notification or observer does not get unregistered.

isKindOfClass returning different values for Test Cases

I have a method someMethod. This method, at some point has the following if-else condition.
- (void) someMethod {
// ... some more code ...
if ([userArray[0] isKindOfClass:[Me class]]) {
// some code
}
else {
// some other code
}
}
Now this if-condition is always met when I execute the code normally. But when I call it from one of my test-cases, the else-part gets executed instead. I am calling this method exactly the same way (it has no side-effects, etc).
When I debugged the thing in both normal run, and testing run. I saw something different.
While running in Test, the userArray had 1 object, (Me_Me_2 *)0x00007fa61d39dbf0.
And while running it normally, the userArray had the same object, but there was one difference. It said (Me_Me_ *)0x00007fce71459ae0.
When I print the value of NSStringFromClass([userArray[0] class]), they both print "Me".
"Me" is a NSManagedObject.
Another interesting thing is, if I add an expression in the debugger and evaluate it, it always evaluates to true - ([((NSObject*)userArray[0]) isKindOfClass:[Me class]]) returns (bool)true. This is totally bizarre! If the condition is true, why does it ever go into the else block?
Now some questions -
What is happening over here? Are Core Data objects treated different when running in tests?
Why is the type of the object "Me_Me_2" while testing and "Me_Me_" otherwise? Why is it not just "Me"?
This sounds similar to the following issue: isKindOfClass doesn't work as expected
In short, is the class being compared a target member of the test target? It should only be a target member of the application.

XCTestExpectation - Calling an async method twice causes API violation

I’m writing unit tests in swifts, and testing a unique workflow.
In methodA(), I load an object incorrectly (say with incorrect credentials) using an async method. Also kick off an expectation
func methodA(withCred credential: NSURLCredential) {
var objA = ObjectA()
// Set objA.a, objA.b, objA.c,
objA.credential = credential //Incorrect credential First time, Correct Credential second time
objA.delegate = self
expectation = expectationWithDescription(“Aync”)
objA.callAsyncMethod() //This fires successDelegate() or failureDelegate()}
When FailureDelegate() is fired, I reload the object, correctly this time. In order do so, I need to call MethodA() again (so I can reuse all the other stuff there).
func failureDelegate(error: NSError!) {
XCTAssertTrue(error.localizedDescription == “Invalid Credentials“)
//Now that I’ve verified correct error is returned, I need to reload objA
methodA(withCred:correctCredential)
}
func successDelegate(obj : ObjectA) {
XCTAssert(“Object is loaded”)
expectation.fulfill()
}
3.This kicks off the same expectation again in methodA, and results in the following error:
API violation - creating expectations while already in waiting mode.
I understand this is not permitted by swift. Is there a workaround or better way to test these kinds of async methods looping with Swift using XCTest?
Thanks!
Don't share instances of expectation across tests. You should be declaring expectation (i.e. with let) in the body of each test, not as a property on XCTestCase. If you really need to use the delegation pattern (closures would be much, much simpler and more conventional), you can pass that as an additional parameter to your delegate method.
I think your code exemple is incomplete, could you provide the full code?
As #mattt said each test should preferably be unique and should not reuse other test variable.
Regarding your issue, you should declare all your expectation first before the triggering waitForExpectationsWithTimeout:handler:. You can't not create a new expectation after you've start waiting for another one.

forwardInvocation to nil object

I have an object (A) that needs to work as a proxy to an other object. there is also a condition that when verified should make the object work as nil.
I've implemented:
-(void)forwardInvocation:(NSInvocation *)anInvocation
{
if (condition)
[anInvocation invokeWithTarget:self.object];
else
[anInvocation invokeWithTarget:nil];
}
but it's not enough. when the condition is satisfied and a method is called on A unrecognized selector sent to instance is raised.
From the documentation on invoke in NSInvocation's class reference:
You must set the receiver’s target, selector, and argument values before calling this method.
If you haven't set target (or have set it to nil, which is basically the same thing), you shouldn't be invoking the invocation. If you want to mimic the behaviour of sending a message to nil, you can just return 0/nil - this is the default behaviour of objc_msgSend if the target is nil. If you're interested in seeing why this works, you can have a look here for an overview of objc_msgSend's implementation (fair warning, it's all in assembly).

performSelector throwing invalid argument, why?

This line of code works perfectly
[self explodeBomb:obj];
but if I replace it with the following line, I get an NSInvalidArgument Exception, with the reason being an unrecognized selector.
[self performSelector:#selector(explodeBomb) withObject:obj ];
The definition of the method is as follows:
-(void)explodeBomb:(SKNode *)bomb
I know, this has to be me not understanding something fundamental. But why I am able to call the method directly with no problems, but when I try to use the performSelector it blows up? For the record obj is defined as an ID. I tried changing the signature of explodeBomb to take an ID and then explicitly cast it inside the method, but that threw the same exception. Anyone know what the heck I am doing wrong?
Use : and write like below
[self performSelector:#selector(explodeBomb:) withObject:obj ];
Since your method explodeBomb has an argument so you have to specify :

Resources