Search code examples
iosocmockgh-unit

Unexpected crash using OCMock, mocking 'mutableCopy' on an NSString


I'm trying to mock a call to mutableCopy using OCMock and GHUnit on iOS.

Despite the test passing, I get an EXC_BAD_ACCESS exception during the cleanup, and I'm trying to work out why.

Take a look at this. This test shows that it is possible to mock mutableCopy on a mock NSString. In this test I return another NSString, not an NSMutableString. This is just to demonstrate that the mutableCopy expectation is fired, and the test passes.

#import <GHUnitIOS/GHUnit.h>
#import "OCMock.h"

@interface TestItClass : GHTestCase @end
@implementation TestItClass

// Test that mutableCopy on an NSString is mockable.
- (void)test_1_mutableCopyOfString_shouldBeMockable_givenAStringIsReturned {
    NSString *string = [OCMockObject mockForClass:NSString.class];
    NSString *copy = @"foo";
    [(NSString *) [[(id) string expect] andReturn:copy] mutableCopy];

    // MutableCopy is mocked to return a string, not a mutable string!
    // This is clearly wrong from a static typing point of view, but
    // the test passes anyway, which is ok.
    NSMutableString *result = [string mutableCopy];
    GHAssertEquals(result, copy, nil);
    [(id)string verify];
}

Now I change the mock expectation so that mutableCopy now returns an NSMutableString. The test still passes, but on the tear down of the test I get a EXC_BAD_ACCESS exception.

- (void)test_2_mutableCopyOfString_shouldBeMockable_givenAMutableStringIsReturned {
    NSString *string = [OCMockObject mockForClass:NSString.class];
    NSMutableString *copy = [@"foo" mutableCopy];
    [(NSString *) [[(id) string expect] andReturn:copy] mutableCopy];

    // Now mutableCopy is mocked to return a mutable string!
    // The test now blows up during the test teardown! Why?
    NSMutableString *foo = [string mutableCopy];
    GHAssertEquals(foo, copy, nil);
    [(id)string verify];
}

@end

In both tests the verifies work, as to the asserts. This shows that both tests are well constructed and that the mock expectations are being fired as expected. However, the second test fails in the tear down with a bad memory access:

Simulator session started with process 7496
Debugger attached to process 7496
2013-03-11 18:23:05.519 UnitTests[7496:c07] TestItClass/test_2_mutableCopyOfString_shouldBeMockable_givenAMutableStringIsReturned ✘ 0.00s
2013-03-11 18:23:06.466 UnitTests[7496:c07] Re-running: TestItClass/test_2_mutableCopyOfString_shouldBeMockable_givenAMutableStringIsReturned <GHTest: 0x7793340>
Exception: EXC_BAD_ACCESS (code=1, address=0x11dfe3ea))

Can you please suggest to me why it might be happening?

Thanks, Joe


Solution

  • the problem your facing is caused by the fact that ARC follows the Basic Memory Management Rules. Specifically this:

    • You own any object you create

      You create an object using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy” (for example, alloc, newObject, or mutableCopy).

    So the solution would be to look at the invocation selector to determine whether to retain the returnValue or not.

    I hope this help.