Модульное тестирование синглтона: макет и пьянство
В настоящее время я пытаюсь протестировать некоторый код, который использует AVAudioSession
и мои попытки издеваться над ним, потому что это единственный, оказались до сих пор трудными, и я провел небольшое исследование и натолкнулся на идею о том, как быстро это получить, это его пример, чтобы затем фактически инициализировать ваш подкласс, как вы этого хотите, но У меня возникли проблемы с выяснением, какие методы можно использовать. Я попробовал sharedInstance и class_addMethod()
возвращает yes за то, что он добавлен, а не заменен. Могу ли я эффективно издеваться над синглтоном таким образом?
@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
1 ответ
Вы могли бы пошли 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
# swizzle 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
}
Некоторая полезная ссылка Правильный путь к Swizzle в Objective-C, метод Swizzling, еще один вопрос в SO