2012-08-12 22 views
7

Tôi đang sử dụng OCMock để tạo mocks trong các thử nghiệm cho ứng dụng iOS của mình và tôi muốn tạo các giao thức không thực hiện tất cả các phương thức tùy chọn.OCMock: Giao thức giả mạo với các phương thức tùy chọn

Nếu nó không rõ ràng những gì tôi có nghĩa là ... đây là một số mã:

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

Hãy để tôi đặt một câu hỏi ngược lại. Tại sao giả lập trả về NO khi được hỏi liệu nó có phản hồi phương thức tùy chọn hay không. Phương thức đó là một phần của giao thức. Có thể giả lập một lớp thực hiện giao thức không? Nếu lớp đó không thực hiện phương thức tùy chọn thì giả lập sẽ làm những gì bạn mong đợi. –

+0

Lý do là tôi muốn thử nghiệm rằng một lớp hoạt động chính xác khi nó có một đại biểu không thực hiện một phương thức ủy nhiệm tùy chọn. – extremeboredom

Trả lời

1

tôi nhấn hạn chế này là tốt. Ý tưởng cơ bản là ghi đè respondsToSelector: (KHÔNG THỂ được OCMock giả mạo một cách đáng tin cậy).

Tôi đã thực hiện lớp học sau đây thực hiện việc này cho bạn. Sau đó bạn có thể sử dụng nó như sau:

mở rộng GCOCMockOptionalMethodSupportingObject, và thực hiện các giao thức của bạn

@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]; 

Nếu bạn không gọi markSelectorAsImplemented, sau đó classBeingTested bạn sẽ nhận được NO cho respondsToSleectorForThatMethod

Tôi đã đặt mã cho nó ở đây. Tôi đang sử dụng điều này để có hiệu quả tuyệt vời. Nhờ jer trên #iphonedev để đặt tôi ra trên con đường này (ghi đè respondsToSelector là ý tưởng của mình, tôi đã làm một số bổ sung phương pháp thời gian chạy điên - đây là methinks sạch hơn nhiều).

đây là các mã

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

Điều dễ nhất để làm là để tạo ra một lớp có chứa các selectors bạn muốn thực hiện. Không cần phải thực hiện bất kỳ. Sau đó, bạn tạo một lớp mô hình của lớp đó thay vì một giao thức giả lập và sử dụng nó chỉ là theo cùng một cách.

Ví dụ:

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

id mock = OCMStrictClassMock([MyAwesomeImplementation class]);