我正在使用 OCMock 3对我的 iOS 项目进行单元测试。
我使用 dispatch_once()
创建了一个单例类 MyManager
:
@implementation MyManager
+ (id)sharedInstance {
static MyManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
我在 School
类中有一个使用上述单例的方法:
@implementation School
...
- (void) createLecture {
MyManager *mgr = [MyManager sharedInstance];
[mgr checkLectures];
...
}
@end
现在,我想对这个方法进行单元测试,我使用了 MyManager 的部分模拟:
- (void) testCreateLecture {
// create a partially mocked instance of MyManager
id partialMockMgr = [OCMockObject partialMockForObject:[MyManager sharedInstance]];
// run method to test
[schoolToTest createLecture];
...
}
- (void)tearDown {
// I want to set the singleton instance to nil, how to?
[super tearDown];
}
在tearDown
阶段,我想将单例实例设置为nil
以便下面的测试用例可以从干净状态开始。
我知道在互联网上,有人建议将 static MyManager *sharedMyManager
移到 +(id)sharedInstance
方法之外。但我想问一下,有没有什么方法可以将实例设置为 nil 而无需将其移到 +(id)sharedInstance
方法之外? (有java反射之类的解决方案吗?)
最佳答案
您无法使用局部静态变量实现您想要的。 block 范围的静态变量仅在其词法上下文中可见。
我们通过使单例实例成为类实现范围内的静态变量并添加一个修改器来覆盖它来实现这一点。通常,该增变器仅由测试调用。
@implementation MyManager
static MyManager *_sharedInstance = nil;
static dispatch_once_t once_token = 0;
+(instancetype)sharedInstance {
dispatch_once(&once_token, ^{
if (_sharedInstance == nil) {
_sharedInstance = [[MyManager alloc] init];
}
});
return _sharedInstance;
}
+(void)setSharedInstance:(MyManager *)instance {
once_token = 0; // resets the once_token so dispatch_once will run again
_sharedInstance = instance;
}
@end
然后在你的单元测试中:
// we can replace it with a mock object
id mockManager = [OCMockObject mockForClass:[MyManager class]];
[MyManager setSharedInstance:mockManager];
// we can reset it so that it returns the actual MyManager
[MyManager setSharedInstance:nil];
这也适用于部分模拟,如您的示例所示:
id mockMyManager = [OCMockObject partialMockForObject:[MyManager sharedInstance]];
[[mockMyManager expect] checkLectures];
[MyManager setSharedInstance:mockMyManager];
[schoolToTest createLecture];
[mockMyManager verify];
[mockMyManager stopMocking];
// reset it so that it returns the actual MyManager
[MyManager setSharedInstance:nil];
关于ios - 在每个测试用例之后将单例实例重置为 nil,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37549661/