2011-12-08 165 views
4

需要为以下代码编写单元测试,我想为类方法canMakePayments做模拟,返回yes或no,到目前为止没有很好的方法找到会费canMakePayments是一个类方法(+),似乎所有的OCMock方法都是用于实例方法( - )。如何模拟类方法(+)?

你们任何建议或讨论将不胜感激。谢谢。

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

点吧,笑? (对不起,我无法抗拒) – Abizern

回答

4

由于您不能通过提供不同的实例来拦截方法,因此您可以为类方法执行的操作是提供不同的类。事情是这样的:

+ (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 

所以你看,这取代了SKPaymentQueueFakePaymentQueue。您的测试现在可以针对TestingSubclass运行。

+1

什么是被测试的班级?也就是说,你是在写一个针对SKPaymentQueue的测试,还是针对另一个调用SKPaymentQueue的类? –

+0

嘿里德;谢谢你的回复。 您的意思是我们需要在我们的测试课程中实施另一个paymentQueueClass?或者破解原有的框架方法类(UnitTests)? 似乎我不完全理解你的解决方案,你能给我更多的提示,非常感谢。 – jianhua

+0

我的测试课是针对不同的课程,调用SKPaymentQueue? – jianhua

12

一种方法是包裹类的方法在自己的实例方法:

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

然后你嘲笑这个方法:

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

啊!非常简单! –

+0

感谢您的回复,是的,代码应该以更容易进行单元测试的方式编写。 – jianhua

+0

非常有帮助,谢谢! – Rahul