在 Objective-C++ 中,为了测试,我如何在运行时使 class nil?
In Objective-C++, for testing, how can I make a class nil at runtime?
情况如下:
在异步函数中,我捕获了一个 weakSelf。
__block auto weakSelf = self;
然后,在块内,我捕获了那个 weakSelf 的 strongSelf。
[_someIvar someAsyncMethod:^{
__strong auto strongSelf = weakSelf;
}];
但是,如果 strongSelf 为 nil,我会进行一些错误处理和报告。
if (!strongSelf) {
_NotifyDelegate(someDeallocationError); // C
}
整个事情:
__block auto weakSelf = self;
[_someIvar someAsyncMethod:^{
__strong auto strongSelf = weakSelf;
if (!strongSelf) {
// How can I trigger this line?
_NotifyDelegate(someDeallocationError); // C
}
}
}];
这都是遗留代码,我正在使用 OCMock 添加单元测试。如何在运行时使 strongSelf 为 nil 并触发该委托通知?
据我所知,您还没有指定任何 class 名称,但假设您有:
@interface SomeClass
…
@end
…
@interface IvarClass
- (void) someAsyncMethod:(void(^)(void))block;
@end
…
@implementation SomeClass
{
IvarClass* _someIvar;
}
…
- (void)someMethod
{
__block auto weakSelf = self;
[_someIvar someAsyncMethod:^{
__strong auto strongSelf = weakSelf;
if (!strongSelf) {
// How can I trigger this line?
_NotifyDelegate(someDeallocationError); // C
}
}];
}
@end
在这种情况下,我会用 StubIvarClassDelayedAsync
替代 class IvarClass
,您可以在其中覆盖 - (void) someAsyncMethod:
,这样块就简单地存储在一个 ivar 中。然后,您可以按照 - (void)completeAsyncMethod
的方式在其上实现另一个方法,该方法调用块。
您需要在测试中使用此 StubIvarClassDelayedAsync
的实例作为您的 _someIvar
- 或者您可以通过 SomeClass
上现有的 API 直接注入它,否则您可能需要 subclass SomeClass
来更改创建生产实例的位置。
在测试方法中将它们放在一起:在 @autoreleasepool
块中,创建 SomeClass
的实例(或其存根子 class),用实例准备它StubIvarClassDelayedAsync
,然后调用 someMethod
。这反过来会导致调用 someAsyncMethod:
的重写版本,它存储块引用。 (您可以断言调用该方法是为了良好的衡量标准。)nil
输出测试中的 SomeClass
指针并关闭池块以删除任何最后的引用。您可能想要断言 dealloc
是在您的 SomeClass instance
上调用的,如果使用虚拟子 class,您可以轻松地做到这一点。但是,请确保保留指向 StubIvarClassDelayedAsync
实例的指针。最后,在该存根实例上调用 completeAsyncMethod
,这应该会导致该块采用错误处理分支。
如果您提供的不仅仅是未命名的片段,我会更具体地说明代码示例,如果您在相应方法中的这些片段之前或之后有代码,那么可能需要考虑这些代码帐户或重构第一。真名也很有用。
情况如下:
在异步函数中,我捕获了一个 weakSelf。
__block auto weakSelf = self;
然后,在块内,我捕获了那个 weakSelf 的 strongSelf。
[_someIvar someAsyncMethod:^{
__strong auto strongSelf = weakSelf;
}];
但是,如果 strongSelf 为 nil,我会进行一些错误处理和报告。
if (!strongSelf) {
_NotifyDelegate(someDeallocationError); // C
}
整个事情:
__block auto weakSelf = self;
[_someIvar someAsyncMethod:^{
__strong auto strongSelf = weakSelf;
if (!strongSelf) {
// How can I trigger this line?
_NotifyDelegate(someDeallocationError); // C
}
}
}];
这都是遗留代码,我正在使用 OCMock 添加单元测试。如何在运行时使 strongSelf 为 nil 并触发该委托通知?
据我所知,您还没有指定任何 class 名称,但假设您有:
@interface SomeClass
…
@end
…
@interface IvarClass
- (void) someAsyncMethod:(void(^)(void))block;
@end
…
@implementation SomeClass
{
IvarClass* _someIvar;
}
…
- (void)someMethod
{
__block auto weakSelf = self;
[_someIvar someAsyncMethod:^{
__strong auto strongSelf = weakSelf;
if (!strongSelf) {
// How can I trigger this line?
_NotifyDelegate(someDeallocationError); // C
}
}];
}
@end
在这种情况下,我会用 StubIvarClassDelayedAsync
替代 class IvarClass
,您可以在其中覆盖 - (void) someAsyncMethod:
,这样块就简单地存储在一个 ivar 中。然后,您可以按照 - (void)completeAsyncMethod
的方式在其上实现另一个方法,该方法调用块。
您需要在测试中使用此 StubIvarClassDelayedAsync
的实例作为您的 _someIvar
- 或者您可以通过 SomeClass
上现有的 API 直接注入它,否则您可能需要 subclass SomeClass
来更改创建生产实例的位置。
在测试方法中将它们放在一起:在 @autoreleasepool
块中,创建 SomeClass
的实例(或其存根子 class),用实例准备它StubIvarClassDelayedAsync
,然后调用 someMethod
。这反过来会导致调用 someAsyncMethod:
的重写版本,它存储块引用。 (您可以断言调用该方法是为了良好的衡量标准。)nil
输出测试中的 SomeClass
指针并关闭池块以删除任何最后的引用。您可能想要断言 dealloc
是在您的 SomeClass instance
上调用的,如果使用虚拟子 class,您可以轻松地做到这一点。但是,请确保保留指向 StubIvarClassDelayedAsync
实例的指针。最后,在该存根实例上调用 completeAsyncMethod
,这应该会导致该块采用错误处理分支。
如果您提供的不仅仅是未命名的片段,我会更具体地说明代码示例,如果您在相应方法中的这些片段之前或之后有代码,那么可能需要考虑这些代码帐户或重构第一。真名也很有用。