objective-c - OCMock:模拟协议(protocol),排除可选方法

标签 objective-c ios tdd ocmock

我正在使用 OCMock 在我的 iOS 应用程序测试中创建模拟,我想创建不实现所有可选方法的协议(protocol)模拟。

如果不清楚我的意思...这里有一些代码:

// 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)];

最佳答案

我也遇到了这个限制。基本思想是覆盖 respondsToSelector:(OCMock 不能可靠地模拟它)。

我制作了以下类(class)来为您完成这项工作。 然后您可以按如下方式使用它:

扩展 GCOCMockOptionalMethodSupportingObject,并实现您的协议(protocol)

@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 将针对 respondsToSleectorForThatMethod 获取 NO

我已经把它的代码放在这里了。我用这个效果很好。感谢 #iphonedev 上的 jer 让我走上了这条路(覆盖 respondsToSelector 是他的想法,我正在做一些疯狂的运行时方法添加 - 这是更清晰的方法)。

这是代码

/**
 * 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

关于objective-c - OCMock:模拟协议(protocol),排除可选方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11923942/

相关文章:

ruby-on-rails - railstutorial.org,第 6 章。未知属性 : password

ios - 以编程方式加载使用 NSCoding 创建的对象模型

ios - 立即从 UITextField 更新 UITextView 字符串

java - 将 PEM 公钥读入 iOS

ios - 在 Swift 中从另一个 viewController 的类访问属性?

ios - Swift:UIGraphicsBeginImageContextWithOptions 比例因子设置为 0 但未应用

ios - Xcode 9 - 无法与辅助应用程序通信

c# - iOS-Unicode取消签名

testing - (错误)理解 Smalltalk 和 TDD

java - 如何使用 TDD 实现 Restful 客户端代码示例