How to mock class method (+)? [duplicate]

2020-03-24 06:38发布

Need to write unit testing for the following code, I want to do mock for class method canMakePayments, return yes or no, so far no good method found dues to canMakePayments is a class method (+), seems all OCMock methods are all used for instance method (-).

You guys any suggestion or discussion will be appreciated. Thanks.

// SKPaymentQueue.h
// StoreKit
if ([SKPaymentQueue canMakePayments]){
   ....
}
else{
   ...
}

2条回答
▲ chillily
2楼-- · 2020-03-24 07:23

Since you can't intercept the method by providing a different instance, what you can do for a class method is provide a different class. Something like this:

+ (Class)paymentQueueClass
{
    return [SKPaymentQueue class];
}

The point of call then becomes:

Class paymentQueueClass = [[self class] paymentQueueClass];
if ([paymentQueueClass canMakePayments])
...

This introduces a "testing seam," or a point of control, allowing us to specify a class other than SKPaymentQueue. Now let's make a replacement:

static BOOL fakeCanMakePayments;

@interface FakePaymentQueue : SKPaymentQueue
@end

@implementation FakePaymentQueue

+ (void)setFakeCanMakePayments:(BOOL)fakeValue
{
    fakeCanMakePayments = fakeValue;
}

+ (BOOL)canMakePayments
{
    return fakeCanMakePayments;
}

@end

Strictly speaking, this isn't a "mock object" -- it's a "fake object." The difference is that a mock object verifies how it's called. A fake object just provides stubbed results.

Now let's create a testing subclass of the original class we want to test.

@interface TestingSubclass : OriginalClass
@end

@implementation TestingSubclass

+ (Class)paymentQueueClass
{
    return [FakePaymentQueue class];
}

@end

So you see, this replaces SKPaymentQueue with FakePaymentQueue. Your tests can now run against TestingSubclass.

查看更多
▲ chillily
3楼-- · 2020-03-24 07:32

One approach is to wrap the class method in your own instance method:

-(BOOL)canMakePayments {
    return [SKPaymentQueue canMakePayments];
}

Then you mock that method:

-(void)testCanHandlePaymentsDisabled {
    Foo *foo = [[Foo alloc] init];
    id mockFoo = [OCMockObject partialMockForObject:foo];
    BOOL paymentsEnabled = NO;
    [[[mockFoo stub] andReturnValue:OCMOCK_VALUE(paymentsEnabled)] canMakePayments];

    // set up expectations for payments disabled case
    ...

    [foo attemptPurchase];
}
查看更多
登录 后发表回答