2015-04-17 2 views
2

В настоящее время я пытаюсь проверить код, который использует AVAudioSession, и мои попытки издеваться над ним, так как это синглтон, пока оказались трудными, и я сделал немного исследований и наткнулся на эту идею для того чтобы swizzly, как это получить, это экземпляр, чтобы фактически инициализировать ваш подкласс так, как вам бы хотелось, но у меня возникли проблемы с выяснением, какие методы для swizzle. Я попробовал sharedInstance, и class_addMethod() возвращает «Да», говоря, что он добавлен, а не заменяет его. Могу ли я эффективно высмеивать синглтон таким образом?Единичное тестирование singleton: mock and swizzle

@interface AVAudioSessionFake : AVAudioSession 

@property (readonly, nonatomic) BOOL wasSetActiveErrorCalled; 

-(instancetype)initStub; 

@end 

@implementation AVAudioSessionFake 

+ (void)load 
{ 
    [AVAudioSessionFake swizzleOriginalMethod:@"sharedInstance" with:@"initStub"]; 
} 

+ (void)swizzleOriginalMethod:(NSString*)Original with:(NSString*)replacement 
{ 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken,^
    { 
     Class class = [self class]; 

     SEL originalSelector = NSSelectorFromString(Original); 
     SEL swizzledSelector = NSSelectorFromString(replacement); 

     Method originalMethod = class_getInstanceMethod(class, originalSelector); 
     Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); 

     BOOL didAddMethod = 
     class_addMethod(class, 
         originalSelector, 
         method_getImplementation(swizzledMethod), 
         method_getTypeEncoding(swizzledMethod)); 

     if (didAddMethod) 
     { 
      class_replaceMethod(class, 
           swizzledSelector, 
           method_getImplementation(originalMethod), 
           method_getTypeEncoding(originalMethod)); 
     } 
     else 
     { 
      method_exchangeImplementations(originalMethod, swizzledMethod); 
     } 
    }); 
} 

-(instancetype)initStub 
{ 
    return [[[self class]alloc]init]; 
} 

- (BOOL)setActive:(BOOL)active error:(NSError *__autoreleasing *)outError 
{ 
    _wasSetActiveErrorCalled = YES; 

    return [super setActive:active error:outError]; 
} 

@end 
+0

Вы хотите окурок 'метода sharedInstance', чтобы вернуть' AVAudioSessionFake', верно? –

+0

Не в конечном счете, пока я могу создать экземпляр 'AVAudioSessionFake', будучи подклассом' AVAudioSession', поэтому я могу проверить, что некоторые методы вызываются, когда они должны. если stubbing sharedInstance достигает уверенности в этом, что я хочу. – Genhain

ответ

0

Вы можете Swizzle sharedInstance метод, чтобы вернуть AVAudioSessionFake как этот

#AVAudioSessionFake:

@interface AVAudioSessionFake : AVAudioSession 
@property (readonly, nonatomic) BOOL wasSetActiveErrorCalled; 

@end 

@implementation AVAudioSessionFake 
-(instancetype)init 
{ 
    if (self == [super init]) { 
     // your init code 
    } 
    return self; 
} 
-(BOOL)setActive:(BOOL)active error:(NSError *__autoreleasing *)outError 
{ 
    _wasSetActiveErrorCalled = YES; 

    return [super setActive:active error:outError]; 
} 
@end 

# надираться sharedInstance:

static Method original_AVAudioSession_SharedInstance_ClassMethod; 
static Method swizzled_AVAudioSession_SharedInstance_ClassMethod; 
@interface AVAudioSession (AVAudioSessionSwizzledSharedInstance) 
@end 
@implementation AVAudioSession (AVAudioSessionSwizzledSharedInstance) 
    + (void)load 
    { 
     static dispatch_once_t onceToken; 
     dispatch_once(&onceToken,^
     { 
      Class class = object_getClass((id)self); 
      SEL original_Selector = @selector(sharedInstance); 
      SEL swizzled_Selector = @selector(swizzled_sharedInstance); 
       original_AVAudioSession_SharedInstance_ClassMethod = class_getInstanceMethod(class, original_Selector); 
      swizzled_AVAudioSession_SharedInstance_ClassMethod = class_getInstanceMethod(class, swizzled_Selector); 
     }); 
    } 
    + (void)swizzling_AVAudioSession_SharedInstance 
    {  
    method_exchangeImplementations(original_AVAudioSession_SharedInstance_ClassMethod,swizzled_AVAudioSession_SharedInstance_ClassMethod); 
    } 
    + (id)swizzled_sharedInstance 
    { 
     static dispatch_once_t p = 0; 
     // initialize sharedObject as nil (first call only) 
     __strong static AVAudioSessionFake _sharedObject = nil; 

     dispatch_once(&p, ^{ 
      _sharedObject = [[AVAudioSessionFake alloc] init]; 
     }); 
     return _sharedObject; 
    } 

# Использование:

- (void)testAnAVAudioSessionMethod { 
    [AVAudioSession swizzling_AVAudioSession_SharedInstance]; //Start swizzling shareInstance method 
    // some test code and assert result here 
    [AVAudioSession swizzling_AVAudioSession_SharedInstance]; //return original method 
} 

Некоторые полезна ссылка The Right Way to Swizzle in Objective-C, method swizzling, another question in SO

+0

Hey Прошу прощения за поздний ответ и спасибо, что нашли время. В конечном итоге я решил просто доверять тому, что яблоко знает, что он делает, и оставлять тестирование некоторых вызываемых методов здесь. Однако я сделал попытку вашего решения, и это не сработало для меня, я боюсь ... Он все еще вернул '+ (id) swizzled_sharedInstance', все еще вернул AVAudioSession, а не подделку – Genhain

Смежные вопросы