如何模拟类方法(+)?

5
需要为以下代码编写单元测试,我想对类方法canMakePayments进行模拟,返回yes或no,由于canMakePayments是一个类方法(+),目前似乎没有好的方法,因为所有OCMock方法都用于实例方法(-)。
您们有任何建议或讨论将不胜感激。谢谢。
// SKPaymentQueue.h
// StoreKit
if ([SKPaymentQueue canMakePayments]){
   ....
}
else{
   ...
}

4
指着它笑?(抱歉,我忍不住了) - Abizern
2个回答

14

一种方法是将类方法包装在您自己的实例方法中:

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

然后你模拟(mock)那个方法:

-(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];
}

谢谢您的回复,是的,代码应该以更易于进行单元测试的方式编写。 - jianhua
非常有帮助,谢谢! - Rahul

6

由于您无法通过提供不同的实例来拦截方法,因此对于类方法,您可以提供一个不同的类。可以像这样操作:

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

那么这里的重点就变成了:
Class paymentQueueClass = [[self class] paymentQueueClass];
if ([paymentQueueClass canMakePayments])
...

这里介绍了一个“测试接缝”,或者说是控制点,让我们能够指定一个类,而不是SKPaymentQueue。现在让我们进行替换:
static BOOL fakeCanMakePayments;

@interface FakePaymentQueue : SKPaymentQueue
@end

@implementation FakePaymentQueue

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

+ (BOOL)canMakePayments
{
    return fakeCanMakePayments;
}

@end

严格来说,这不是一个“模拟对象”-它是一个“假对象”。区别在于,模拟对象会验证其被调用的方式。假对象仅提供存根结果。
现在让我们创建原始类的测试子类。
@interface TestingSubclass : OriginalClass
@end

@implementation TestingSubclass

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

@end

所以你可以看到,这里用FakePaymentQueue替换了SKPaymentQueue。现在你的测试可以针对TestingSubclass运行。

1
被测试的类是什么?也就是说,你是针对SKPaymentQueue编写测试,还是针对调用SKPaymentQueue的其他类编写测试? - Jon Reid
嗨,Reid;谢谢你的友善回复。 你是说我们需要在我们的测试类中实现另一个paymentQueueClass吗?还是直接修改原始框架方法类别(UnitTests)? 似乎我还没有完全理解你的解决方案,你能给我更多提示吗?非常感谢。 - jianhua
我的测试类针对调用SKPaymentQueue的不同类? - jianhua
是的,你说得对,我们可以使用假方法来解决这种棘手的问题。 - jianhua

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接