2012-08-12 17 views
7

Używam OCMock do tworzenia mocks w moich testach dla mojej aplikacji na iOS, i chciałbym stworzyć mocks protokołów, które nie implementują wszystkich opcjonalnych metod.OCMock: Wyśmiewające protokoły z wykluczeniem metod opcjonalnych

Jeśli to nie jest jasne, co mam na myśli ... Oto niektóre kodu:

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

Zadam pytanie w zamian. Dlaczego fałszywiec powinien zwracać NO, gdy pojawi się pytanie, czy odpowiada on metodzie opcjonalnej. Ta metoda jest mimo wszystko częścią protokołu. Czy można kpić z klasy, która implementuje protokół? Jeśli ta klasa nie wdroży metody opcjonalnej, to pozorowanie zrobi to, czego oczekujesz. –

+0

Powodem jest to, że chciałbym przetestować, czy klasa zachowuje się poprawnie, gdy ma delegata, który nie implementuje opcjonalnej metody delegatów. – extremeboredom

Odpowiedz

1

uderzę tego ograniczenia, jak również. Podstawową ideą jest zastąpienie respondsToSelector: (której NIE MOŻNA niezawodnie wyśmiewać przez OCMock).

Zrobiłem następującą klasę, która robi to za Ciebie. można następnie używać go w sposób następujący:

przedłużyć GCOCMockOptionalMethodSupportingObject i zaimplementować protokół

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

Jeśli nie zadzwonić markSelectorAsImplemented, wówczas classBeingTested dostanie NIE dla respondsToSleectorForThatMethod

Włożyłam kod dla tego tutaj. Używam tego do świetnego efektu. Dziękuję jerkowi za #iphonedeva za ustawienie mnie na tej ścieżce (przesłonięcie respondsToSelector było jego pomysłem, robiłem szalony dodatek do metody runtime - jest to o wiele czystsze metinks).

oto kod

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

Najłatwiejszą rzeczą do zrobienia jest, aby utworzyć klasę zawierającą selektorów Ty chcesz wdrożone. Nie wymaga żadnej implementacji. Następnie tworzysz makietę klasy tej klasy zamiast próbnego protokołu i używasz jej w ten sam sposób.

Na przykład:

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

id mock = OCMStrictClassMock([MyAwesomeImplementation class]); 
Powiązane problemy