2012-08-12 11 views
7

Estoy utilizando OCMock para crear simulaciones en mis pruebas para mi aplicación iOS, y me gustaría crear simulaciones de protocolos que no implementen todos los métodos opcionales.OCMock: Protocolos de burla con la exclusión de métodos opcionales

Si no está claro lo que quiero decir ... aquí hay algo de código:

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

Déjeme hacerle una pregunta a cambio. ¿Por qué debería el simulacro devolver NO cuando se le preguntó si responde al método opcional? Ese método es parte del protocolo después de todo. ¿Sería posible burlarse de una clase que implementa el protocolo? Si esa clase no implementa el método opcional, entonces el simulacro haría lo que usted espera. –

+0

La razón es que me gustaría probar que una clase se comporta correctamente cuando tiene un delegado que no implementa un método delegado opcional. – extremeboredom

Respuesta

1

llegué a esta limitación también. La idea básica es anular respondsToSelector: (OCMock NO PUEDE burlarlo de manera confiable).

Hice la siguiente clase que hace esto por usted. continuación, puede utilizar la siguiente manera:

extienden GCOCMockOptionalMethodSupportingObject, e implementar el protocolo de

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

Si usted no llama markSelectorAsImplemented, entonces su classBeingTested no obtendrá para respondsToSleectorForThatMethod

He puesto el código para esto aquí. Estoy usando esto con gran efecto. Gracias a jer on #iphonedev por ponerme en este camino (sobreescribiendo respondsToSelector fue su idea, estaba haciendo un método de ejecución loco además, esto es mucho más limpio).

Aquí está el código

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

La cosa más fácil que hacer es crear una clase que contiene los selectores te quiero implementado. No es necesario que haya ninguna implementación. Luego, crea un simulacro de clase de esa clase en lugar de un simulacro de protocolo y lo usa de la misma manera.

Por ejemplo:

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

id mock = OCMStrictClassMock([MyAwesomeImplementation class]); 
Cuestiones relacionadas