2012-08-12 43 views
7

我使用OCMock在我的iOS應用程序的測試中創建嘲笑,並且我想創建不執行所有可選方法的協議的模擬。OCMock:剔除可選方法的嘲諷協議

如果還不清楚我是什麼意思?這裏的一些代碼:

// Protocol definition 
@protocol MyAwesomeProtocol 
    - (void)doThatRequiredThing; 
    @optional 
    - (void)doThatOptionalThing; 
@end 

... 

// In a test 
id mock = [OCMockObject mockObjectForProtocol:@protocol(MyAwesomeProtocol)]; 

// This should return YES: 
[mock respondsToSelector:@selector(doThatRequiredThing)]; 
// This should return NO: 
[mock respondsToSelector:@selector(doThatOptionalThing)]; 
+1

讓我問一個問題作爲回報。當被問及是否響應可選方法時,爲什麼模擬應該返回NO。畢竟,這個方法是協議的一部分。 是否可以模擬實現該協議的類?如果這個類沒有實現可選方法,那麼模擬會做你期望的。 – 2012-08-13 10:30:04

+0

原因是我想測試一個類,當它有一個沒有實現可選委託方法的委託時,它的行爲是正確的。 – extremeboredom 2012-08-13 14:07:58

回答

1

我打這個限制也是如此。基本思想是覆蓋respondsToSelector :(不能被OCMock可靠地嘲笑)。

我做了以下這樣的課,爲你做到這一點。 然後,您可以使用它,如下所示:

延長GCOCMockOptionalMethodSupportingObject,並實現你的協議

@interface GCTestDelegate : GCOCMockOptionalMethodSupportingObject <GCDelegate> 
@end 

@implementation GCTestDelegate 

//required methods 
- (void)requiredMethod{ 
} 
@end 

// create your testdelegate 
self.classBeingTested.delegate = [OCMock partialMockForObject:[GCTestDelegate new]]; 
[self.classBeingTested.delegate markSelectorAsImplemented:@selector(optionalMethod:)]; 
[[self.classBeingTested.delegate expect] optionalMethod:self.classBeingTested]; 
[self.classBeingTested doSomethingThatwillCheckIfYourDelegateRespondsToYourOptionalMethod]; 

如果你不叫markSelectorAsImplemented,那麼你的classBeingTested將獲得NO的respondsToSleectorForThatMethod

我已經把這裏的代碼。我使用這個效果很好。感謝#iphonedev上的jer讓我在這條路上脫穎而出(重寫respondsToSelector是他的想法,我正在做一些瘋狂的運行時方法增加 - 這是更清潔的methinks)。

這裏的代碼

/** 
* This class is specifically useful and intended for testing code paths that branch 
* pending implementation of optional methods. 
* OCMock does not support mocking of protocols with unimplemented optional methods. 
* Further compounding the issue is the fact that OCMock does not allow mocking of 
* respondsToSelector (in fact, it does but the behaviour is undefined), 
* As such this class can be extending to implement a given protocol, the methods can be mocked/expected 
* as normal, but in addition we can tell the class to report it conforms to a protocol method or not. 
* 
*/ 
@interface GCOCMockOptionalMethodSupportingObject : NSObject 

- (void)markSelectorAsImplemented:(SEL)aSelector; 
- (void)unmarkSelectorAsImplemented:(SEL)aSelector; 


@end 

#import "GCOCMockOptionalMethodSupportingObject.h" 


@interface GCOCMockOptionalMethodSupportingObject() 
@property(nonatomic, strong) NSMutableArray *implementedSelectors; 

@end 

@implementation GCOCMockOptionalMethodSupportingObject { 

} 
////////////////////////////////////////////////////////////// 
#pragma mark init 
////////////////////////////////////////////////////////////// 

- (id)init { 
    self = [super init]; 
    if (self) { 
     self.implementedSelectors = [NSMutableArray array]; 
    } 

    return self; 
} 

////////////////////////////////////////////////////////////// 
#pragma mark public api 
////////////////////////////////////////////////////////////// 


- (void)markSelectorAsImplemented:(SEL)aSelector { 
    if (![self isImplemented:aSelector]) { 
     [self.implementedSelectors addObject:NSStringFromSelector(aSelector)]; 
    } 
} 


- (void)unmarkSelectorAsImplemented:(SEL)aSelector { 
    for (NSString *selectorValue in [self.implementedSelectors mutableCopy]) { 
     SEL storedSelector = NSSelectorFromString(selectorValue); 
     if (sel_isEqual(aSelector, storedSelector)) { 
      [self.implementedSelectors removeObject:selectorValue]; 
      break; 
     } 
    } 
} 


////////////////////////////////////////////////////////////// 
#pragma mark private impl 
////////////////////////////////////////////////////////////// 


- (BOOL)isImplemented:(SEL)aSelector { 
    for (NSString *selectorValue in self.implementedSelectors) { 
     SEL storedSelector = NSSelectorFromString(selectorValue); 
     if (sel_isEqual(aSelector, storedSelector)) { 
      return YES; 
     } 
    } 
    return NO; 
} 

////////////////////////////////////////////////////////////// 
#pragma mark overridden 
////////////////////////////////////////////////////////////// 

- (BOOL)respondsToSelector:(SEL)aSelector { 
    if ([self isImplemented:aSelector]) { 
     return YES; 
    } else { 
     return [super respondsToSelector:aSelector]; 
    } 
} 

@end 
1

做的最簡單的事情是創建一個包含你想實現的選擇一類。不需要任何實現。然後,您創建該類的類模擬而不是協議模擬,並以相同的方式使用它。

例如:

@interface MyAwesomeImplementation : NSObject <MyAwesomeProtocol> 
- (void)doThatRequiredThing; 
@end 
@implementation MyAwesomeImplementation 
- (void)doThatRequiredThing {} 
@end 

id mock = OCMStrictClassMock([MyAwesomeImplementation class]);