ios - 在每个测试用例之后将单例实例重置为 nil

标签 ios objective-c unit-testing singleton ocmock

我正在使用 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];

Here's a full breakdown of the approach.

关于ios - 在每个测试用例之后将单例实例重置为 nil,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37549661/

相关文章:

javascript - 使用 Jasmine 监视没有对象的函数

ios - 为什么这个连接不起作用?

ios - 如何在 iOS 上显示仪表/SpeedView (Xcode)

iPhone 网络应用程序的 jQuery Mobile 高度问题,假定页面高度为 480 而不是 460

ios - NSArray 错误 : NSCFArray objectAtIndex index (5) beyond bounds (5)

ios - 在 ios 中实现 Creative SDK for Image Effects 时出现链接器错误

ios - 如何按 id 或月份 : Descending (Latest Post First)? 对数组进行排序

c++ - 在 Objective-C 中使用 fopen()

python - 使用不同的配置运行相同的测试

c++ - 在 Boost Test 框架中测试 assert