简体   繁体   中英

OCMock - accessing weak properties of a partially mocked object causes sporadic crash

I'm using OCMock to test UIViewControllers that are instantiated from a storyboard. Following best practice, all IBOutlets of subviews of the view controller's view are weak properties. When I'm writing tests for these view controllers in which the tests call methods that rely on the weak properties, I'm seeing sporadic EXC_BAD_ACCESS crashes like the one I attached.

The crash does not happen every unit test run. There does not seem to be a pattern, it's totally random whether the crash surfaces or not. This design technique of partially mocking the view controller is used in many places in the app as well.

What could be going on? Is there a conflict in what I'm trying to do here?

@interface CustomUIViewControllerTests : XCTestCase

@property (nonatomic, strong) CustomUIViewController *vc;
@property (nonatomic, strong) UIStoryboard *mainStoryboard;

@end

@implementation CustomUIViewControllerTests

- (void)setUp
{
    [super setUp];

    // In order to test data on the Storyboard - need to instantiate the Storyboard
    self.mainStoryboard = [UIStoryboard storyboardWithName:[[NSBundle mainBundle].infoDictionary
                                                            objectForKey:@"UIMainStoryboardFile"]
                                                    bundle:[NSBundle mainBundle]];

    self.vc = [_mainStoryboard instantiateViewControllerWithIdentifier:kStoryboardVCID];
    [self.vc loadView];
}

- (void)testSomething
{
    id mockController = [OCMockObject partialMockForObject:self.vc];
    [mockController doSomethingThatUsesAWeakProperty];
    //other verification/assertions here
}

The Crash:

Process:         XXXX [33756]
Path:            /Users/USER/Library/Application Support/iPhone Simulator/*/XXXX.app/XXXX
Identifier:      XXXX
Version:         0
Code Type:       X86-64 (Native)
Parent Process:  launchd_sim [33727]
Responsible:     launchd_sim [33727]
User ID:         501

Date/Time:       2014-07-10 11:51:51.692 -0400
OS Version:      Mac OS X 10.9.3 (13D65)
Report Version:  11
Anonymous UUID:  8B81673C-B92D-28D3-9940-47C83C140C8E


Crashed Thread:  0  Dispatch queue: com.apple.main-thread

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x000000011ab0e360

External Modification Warnings:
Debugger attached to process.

VM Regions Near 0x11ab0e360:
    MALLOC_TINY            0000000114700000-0000000114800000 [ 1024K] rw-/rwx SM=PRV  
--> 
    JS JIT generated code  0000056193e99000-0000056193e9a000 [    4K] ---/rwx SM=NUL  

Application Specific Information:
iPhone Simulator 463.9.41, iPhone OS 7.1 (iPhone Retina (4-inch 64-bit)/11D167)


Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib              0x0000000103156292 __kill + 10
1   XXXX                                0x000000010010f565 CLSSignalHandler + 218 (CLSSignal.m:305)
2   libsystem_platform.dylib            0x00000001030295aa _sigtramp + 26
3   Foundation                          0x0000000102174225 -[NSConcreteValue getValue:] + 29
4   Foundation                          0x00000001021af5be -[NSValue nonretainedObjectValue] + 25
5   XXXX                                0x0000000100153e00 +[OCPartialMockObject existingPartialMockForObject:] + 160 (OCPartialMockObject.m:50)
6   XXXX                                0x00000001001546e1 -[OCPartialMockObject forwardingTargetForSelectorForRealObject:] + 65 (OCPartialMockObject.m:170)
7   CoreFoundation                      0x0000000102a8ba5c ___forwarding___ + 156
8   CoreFoundation                      0x0000000102a8b938 _CF_forwarding_prep_0 + 120
9   XXXX                                0x0000000100028ea6 -[CustomUIViewController doSomethingThatUsesAWeakProperty] + 454 (CustomUIViewControllerTests.m:121)
10  CoreFoundation                      0x0000000102a8ff1c __invoking___ + 140
11  CoreFoundation                      0x0000000102a8fdc4 -[NSInvocation invoke] + 308
12  CoreFoundation                      0x0000000102a8ff86 -[NSInvocation invokeWithTarget:] + 54
13  XXXX                                0x0000000100154999 -[OCPartialMockObject handleUnRecordedInvocation:] + 73 (OCPartialMockObject.m:217)
14  XXXX                                0x0000000100151fe6 -[OCMockObject forwardInvocation:] + 102 (OCMockObject.m:190)
15  CoreFoundation                      0x0000000102a8bb85 ___forwarding___ + 453
16  CoreFoundation                      0x0000000102a8b938 _CF_forwarding_prep_0 + 120
17  XXXXTests                           0x000000010c0d289d -[CustomUIViewControllerTests testSomething] + 573 (CustomUIViewControllerTests.m:996)
18  CoreFoundation                      0x0000000102a8ff1c __invoking___ + 140
19  CoreFoundation                      0x0000000102a8fdc4 -[NSInvocation invoke] + 308
20  XCTest                              0x0000000109ba4c40 -[XCTestCase invokeTest] + 161
21  XCTest                              0x0000000109ba4d2c -[XCTestCase performTest:] + 91
22  XCTest                              0x0000000109ba5a75 -[XCTest run] + 65
23  XCTest                              0x0000000109ba44df -[XCTestSuite performTest:] + 125
24  XCTest                              0x0000000109ba5a75 -[XCTest run] + 65
25  XCTest                              0x0000000109ba44df -[XCTestSuite performTest:] + 125
26  XCTest                              0x0000000109ba5a75 -[XCTest run] + 65
27  XCTest                              0x0000000109ba44df -[XCTestSuite performTest:] + 125
28  XCTest                              0x0000000109ba5a75 -[XCTest run] + 65
29  XCTest                              0x0000000109ba71b4 +[XCTestProbe runTests:] + 138
30  Foundation                          0x00000001021846dc __NSFireDelayedPerform + 354
31  CoreFoundation                      0x0000000102a5cc34 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
32  CoreFoundation                      0x0000000102a5c7b2 __CFRunLoopDoTimer + 962
33  CoreFoundation                      0x0000000102a457be __CFRunLoopRun + 1614
34  CoreFoundation                      0x0000000102a44d83 CFRunLoopRunSpecific + 467
35  GraphicsServices                    0x00000001039dcf04 GSEventRunModal + 161
36  UIKit                               0x00000001010cce33 UIApplicationMain + 1010
37  XXXX                                0x000000010007b889 main + 169 (main.m:16)
38  libdyld.dylib                       0x000000010301f5fd start + 1

The problem here has to be associated with what OCMock does when creating a partialMock. You can check the source code here .

It's likely that your ViewController's view is being nilled out, and with it, all of it's subviews (which are set as outlets)

But this is important: normally you mock other objects, not your system under test. If you're mocking your SUT, it may be be a sign that you need to refactor your code.

Having said that, I mock the SUT using partial mock for things like returning a mock object from a lazy-instantiated property for example and that has worked perfectly fine.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM