简体   繁体   中英

Using OCMock to mock [[UIDevice currentDevice] userInterfaceIdiom]

I am learning to use OCMocks with my unit tests and I understand the basics. However, I'm not sure how I could mock calls to class methods.

I'd like to have:

[[UIDevice currentDevice] userInterfaceIdiom]

return different (but valid) interface idioms for my test cases.

id mock = [OCMockObject mockForClass:[UIDevice class]];

// Would I use a mock or a stub to have UIDevice mock return
// currentDevice and then from it, return userInterfaceIdiom

Mocking singletons is tricky. You can do it with some runtime magic. It's possible to do with the help of Matt Gallagher's invokeSupersequent macro . Basically, you add a category to your test case that overrides currentDevice to return a mock, only if you've set the mock. Here's the setup:

#import "NSObject+SupersequentImplementation.h"

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"

@implementation UIDevice (UnitTests)

+(id)currentDevice {
    if ([BaseTestCase mockDevice]) {
        return [BaseTestCase mockDevice];
    }

    return invokeSupersequentNoArgs();
}

@end

#pragma clang diagnostic pop


static id mockDevice = nil;

+(id)mockDevice {
    return mockDevice;
}

+(id)createMockDevice {
    mockDevice = [OCMockObject mockForClass:[UIDevice class]];
    return mockDevice;
}

+(id)createNiceMockDevice {
    mockDevice = [OCMockObject niceMockForClass:[UIDevice class]];
    return mockDevice;
}

-(void)tearDown {
    mockDevice = nil;
    [super tearDown];
}

Then, in your test:

-(void)testShouldDoSomethingOnIpad {
    id mockDevice = [BaseTestCase createNiceMockDevice];
    [[[mockDevice stub] andReturnValue:OCMOCK_VALUE(UIUserInterfaceIdiomPad)] userInterfaceIdiom];

    // do something iPad-specific

    [mockDevice verify];
}

I did a more detailed write-up of this approach a while back.

Here's a simple solution:

OCMockObject* _deviceMock = OCMPartialMock([UIDevice currentDevice]);

[[[_deviceMock stub] andReturnValue:@(UIUserInterfaceIdiomPad)] userInterfaceIdiom];

Just make sure the code in your behavior uses this check

[UIDevice currentDevice].userInterfaceIdiom

INSTEAD of the macro

UI_USER_INTERFACE_IDIOM()

An alternate approach is to put both variants in your tests, and run the tests in the iPhone and iPad simulators. Admittedly, this is a bit of a hassle.

-(void)testShouldDoSomething {
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        // test iPad behavior
    } else {
        // test iPhone behavior
    }
}

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