XCTest 测试方法中的 KVO addObserver 逻辑崩溃
Posted
技术标签:
【中文标题】XCTest 测试方法中的 KVO addObserver 逻辑崩溃【英文标题】:KVO addObserver logic crashes in XCTest test method 【发布时间】:2013-12-31 00:08:45 【问题描述】:我在结合 XCTest 使用键值观察逻辑时遇到了一些困难(原始代码正在通过测试覆盖率进行改造)。该逻辑在正常(非测试)上下文中运行良好,但每次在测试上下文中都会出现异常。
逻辑的要点是——我有两个类,分别称为 Service 和 Helper。脚手架实现是:
interface Service : NSObject
BOOL svcCallComplete;
@end
@implementation Service
- (id) init
if ((self=[super init])==nil)
return nil;
return self;
@end
interface Helper : NSObject
@end
@implementation Helper
- (id) init
if ((self=[super init])==nil)
return nil;
return self;
@end
Helper 是 Service 中某个属性的观察者。在我的正常运行时逻辑的上下文中,我通过调用服务实例方法addSvcObserver
来做到这一点:
Service.m:
- (void) addSvcObserver:(id)observer
[self addObserver:observer
forKeyPath:@"svcCallComplete"
options:NSKeyValueObservingOptionNew
context:nil];
Helper 遵循 KVO 观察模式,因此:
Helper.m:
- (void) observeValueForKeyPath:(NSString*)keyPath
ofObject:(id)object
change:(NSDictionary*)change
context:(void*)context
非常直截了当,我不会进入监控属性更改的逻辑,因为问题发生在此之前——如果我有如下代码摘录:
Service* service = [[Service alloc] init];
Helper* helper = [[Helper alloc] init];
[service addSvcObserver:helper];
在非测试用例中没有问题(即,这个和相关的 KVO 逻辑按预期工作)。但是,在 XCTest 测试方法的上下文中执行 addSvcObserver
调用会产生立即访问被拒绝异常。
我已经包含了一个异常“全部中断”断点——在addObserver:forKeyPath:options:context:
调用期间,问题似乎出现在objc_registerClassPair
中。测试目标明确禁用了 ARC,因为它提供测试覆盖的项目(目标是 ios7)由于遗留原因是非 ARC;这似乎不会对其他测试造成任何问题。
想法?
【问题讨论】:
【参考方案1】:interface Service : NSObject
@property (nonatomic) BOOL svcCallComplete;
您应该将 svcCallComplete 声明为属性。
因为对于您希望观察的属性,观察到的类必须是键值观察兼容的
我认为你得到objc_registerClassPair
的原因可能是因为KVO 动态注册了Service
的子类,但找不到svcCallComplete
的setter 方法。动态子类需要覆盖该setter 方法并发送通知。
更多详情read this。
【讨论】:
我确实尝试过使用属性语法,但并没有什么不同。不过,感谢您的链接——Mike 的好文章,它为我指明了正确的方向。正如他在那篇文章中指出的那样,使用手动更改通知时实际上不需要设置器。请参阅我的答案以了解实际原因。【参考方案2】:结果证明是我的 KVO 逻辑实现不完整。根据指南here,您必须在使用手动更改通知时覆盖automaticallyNotifiesObserversForKey:
的NSObject
实现——我在最初阅读文本时不知何故错过了这一点。我在Service
类中添加了以下内容:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey
BOOL automatic = NO;
if ([theKey isEqualToString:@"svcCallComplete"])
automatic = NO;
else
automatic = [super automaticallyNotifiesObserversForKey:theKey];
return automatic;
现在测试用例中的一切都是正确的。有谁愿意冒险猜测为什么这在正常(非测试)情况下没有爆炸?
【讨论】:
您是否在应用程序中设置了异常断点,或者您是否正在查看它记录到控制台的内容?异常可能已被运行循环消耗。 -- 没关系,我看到你说的是访问异常(即 Mach 异常)而不是 ObjC 异常。以上是关于XCTest 测试方法中的 KVO addObserver 逻辑崩溃的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 XCTest UI 测试区分 iOS 13 中的标题和静态文本