2012-08-12 41 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]);