Ios 用Kiwi测试核心数据实体的后台保存
我正在努力找出在后台线程中测试与核心数据交互的最佳方法。我有以下的类方法:Ios 用Kiwi测试核心数据实体的后台保存,ios,unit-testing,core-data,grand-central-dispatch,kiwi,Ios,Unit Testing,Core Data,Grand Central Dispatch,Kiwi,我正在努力找出在后台线程中测试与核心数据交互的最佳方法。我有以下的类方法: + (void)fetchSomeJSON { // Download some json then parse it in the block [[AFHTTPClient sharedClient] fetchAllThingsWithCompletion:^(id results, NSError *error) { if ([results count] > 0) {
+ (void)fetchSomeJSON
{
// Download some json then parse it in the block
[[AFHTTPClient sharedClient] fetchAllThingsWithCompletion:^(id results, NSError *error) {
if ([results count] > 0) {
NSManagedObjectContext *backgroundContext = //... create a new context for background insertion
dispatch_queue_t background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(background, ^{ // If I comment this out, my test runs just fine
//... insert and update some entities
for (NSString *str in results) {
NSManagedObject *object = //...
}
});
}
}];
}
我目前正在使用以下Kiwi代码测试此方法:
describe(@"MyAction", ^{
__block void (^completionBlock)(NSArray *array, NSError *error);
beforeEach(^{
// Stub the http client
id mockClient = [AFHTTPClient mock];
[WRNAPIClient stub:@selector(sharedClient) andReturn:mockClient];
// capture the block argument
KWCaptureSpy *spy = [mockClient captureArgument:@selector(fetchAllThingsWithCompletion:) atIndex:0];
[MyClass fetchSomeJSON]; // Call the method so we can capture the block
completionBlock = spy.argument;
// run the completion block
completionBlock(@[@"blah"], nil);
})
// If I remove the dispatch_async block, this test passes fine.
// If I add it in again the test fails, probably because its not waiting
it(@"should return the right count", ^{
// entityCount is a block that performs a fetch request count
NSInteger count = entityCount(moc, @"Task");
[[theValue(count) should] equal:theValue(4)];
})
// This works fine, but obviously I don't want to wait a second
it(@"should return the right count after waiting for a second", ^{
sleep(1);
NSInteger count = entityCount(moc, @"Task");
[[theValue(count) should] equal:theValue(4)];
});
};
如果我删除
dispatch\u async
行,那么我可以让测试快速运行。在使用dispatch\u async
时,让测试套件运行的唯一方法是在调用完成块后执行sleep(1)
。使用sleep()。我曾经尝试过使用应该最终
,但这似乎并没有重新获取我的计数
值。我尝试过几种方法来解决这个问题,没有一种感觉是正确的
1)将dispatch\u async
移动到自己的类中
+ (void)dispatchOnMainQueue:(Block)block
{
if ([NSThread currentThread] == [NSThread mainThread]) {
block();
} else {
dispatch_sync(dispatch_get_main_queue(), block);
}
}
+ (void)dispatchOnBackgroundQueue:(Block)block
{
dispatch_queue_t background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(background, block);
}
然后,在测试执行期间,将后台调度切换到主队列上。这是可行的,但不可预测。我也觉得很不对
2)将设置代码移动到Kiwi
之前的块,然后休眠主线程。
这是因为Kiwi
测试在主线程上运行,所以我们实际上是在说“在进行测试之前让后台操作发生”。我想这就是我要用的。是的,它使我的单元测试运行得更慢,但它们在应该通过的时候通过,在应该通过的时候失败
describe(@"MyAction", ^{
__block void (^completionBlock)(NSArray *array, NSError *error);
beforeAll(^{
// Stub the http client
id mockClient = [AFHTTPClient mock];
[WRNAPIClient stub:@selector(sharedClient) andReturn:mockClient];
// capture the block argument
KWCaptureSpy *spy = [mockClient captureArgument:@selector(fetchAllThingsWithCompletion:) atIndex:0];
[WRNTaskImporter importAllTasksFromAPI];
completionBlock = spy.argument;
// run the completion block
completionBlock(@[@"blah"], nil);
// Wait for background import to complete
[NSThread sleepForTimeInterval:0.1];
})
// This works
it(@"should return the right count", ^{
// entityCount is a block that performs a fetch request count
NSInteger count = entityCount(moc, @"Task");
[[theValue(count) should] equal:theValue(4)];
})
};
这种方法的警告是,只有在测试前不更改任何数据时,它才起作用。例如,假设我插入4个实体,并希望检查每个实体是否按预期插入。这个选项在这里会起作用。如果需要重新运行导入方法并检查计数是否增加,则需要在调用插入代码后添加另一个[NSThread sleepForTimeInterval:0.1]
对于基于正常块的Kiwi
测试,您可能应该使用expectFutureValue-shouldneally
方法或KWCaptureSpy
来测试代码,但这在调用嵌套块时可能没有帮助
如果有人有一个更合适的方法来测试像这样的案例,我很高兴听到它 我尝试了几种方法来解决这个问题,但没有一种是正确的
1)将dispatch\u async
移动到自己的类中
+ (void)dispatchOnMainQueue:(Block)block
{
if ([NSThread currentThread] == [NSThread mainThread]) {
block();
} else {
dispatch_sync(dispatch_get_main_queue(), block);
}
}
+ (void)dispatchOnBackgroundQueue:(Block)block
{
dispatch_queue_t background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(background, block);
}
然后,在测试执行期间,将后台调度切换到主队列上。这是可行的,但不可预测。我也觉得很不对
2)将设置代码移动到Kiwi
之前的块,然后休眠主线程。
这是因为Kiwi
测试在主线程上运行,所以我们实际上是在说“在进行测试之前让后台操作发生”。我想这就是我要用的。是的,它使我的单元测试运行得更慢,但它们在应该通过的时候通过,在应该通过的时候失败
describe(@"MyAction", ^{
__block void (^completionBlock)(NSArray *array, NSError *error);
beforeAll(^{
// Stub the http client
id mockClient = [AFHTTPClient mock];
[WRNAPIClient stub:@selector(sharedClient) andReturn:mockClient];
// capture the block argument
KWCaptureSpy *spy = [mockClient captureArgument:@selector(fetchAllThingsWithCompletion:) atIndex:0];
[WRNTaskImporter importAllTasksFromAPI];
completionBlock = spy.argument;
// run the completion block
completionBlock(@[@"blah"], nil);
// Wait for background import to complete
[NSThread sleepForTimeInterval:0.1];
})
// This works
it(@"should return the right count", ^{
// entityCount is a block that performs a fetch request count
NSInteger count = entityCount(moc, @"Task");
[[theValue(count) should] equal:theValue(4)];
})
};
这种方法的警告是,只有在测试前不更改任何数据时,它才起作用。例如,假设我插入4个实体,并希望检查每个实体是否按预期插入。这个选项在这里会起作用。如果需要重新运行导入方法并检查计数是否增加,则需要在调用插入代码后添加另一个[NSThread sleepForTimeInterval:0.1]
对于基于正常块的Kiwi
测试,您可能应该使用expectFutureValue-shouldneally
方法或KWCaptureSpy
来测试代码,但这在调用嵌套块时可能没有帮助
如果有人有一个更合适的方法来测试像这样的案例,我很高兴听到它 我尝试了几种方法来解决这个问题,但没有一种是正确的
1)将dispatch\u async
移动到自己的类中
+ (void)dispatchOnMainQueue:(Block)block
{
if ([NSThread currentThread] == [NSThread mainThread]) {
block();
} else {
dispatch_sync(dispatch_get_main_queue(), block);
}
}
+ (void)dispatchOnBackgroundQueue:(Block)block
{
dispatch_queue_t background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(background, block);
}
然后,在测试执行期间,将后台调度切换到主队列上。这是可行的,但不可预测。我也觉得很不对
2)将设置代码移动到Kiwi
之前的块,然后休眠主线程。
这是因为Kiwi
测试在主线程上运行,所以我们实际上是在说“在进行测试之前让后台操作发生”。我想这就是我要用的。是的,它使我的单元测试运行得更慢,但它们在应该通过的时候通过,在应该通过的时候失败
describe(@"MyAction", ^{
__block void (^completionBlock)(NSArray *array, NSError *error);
beforeAll(^{
// Stub the http client
id mockClient = [AFHTTPClient mock];
[WRNAPIClient stub:@selector(sharedClient) andReturn:mockClient];
// capture the block argument
KWCaptureSpy *spy = [mockClient captureArgument:@selector(fetchAllThingsWithCompletion:) atIndex:0];
[WRNTaskImporter importAllTasksFromAPI];
completionBlock = spy.argument;
// run the completion block
completionBlock(@[@"blah"], nil);
// Wait for background import to complete
[NSThread sleepForTimeInterval:0.1];
})
// This works
it(@"should return the right count", ^{
// entityCount is a block that performs a fetch request count
NSInteger count = entityCount(moc, @"Task");
[[theValue(count) should] equal:theValue(4)];
})
};
这种方法的警告是,只有在测试前不更改任何数据时,它才起作用。例如,假设我插入4个实体,并希望检查每个实体是否按预期插入。这个选项在这里会起作用。如果需要重新运行导入方法并检查计数是否增加,则需要在调用插入代码后添加另一个[NSThread sleepForTimeInterval:0.1]
对于基于正常块的Kiwi
测试,您可能应该使用expectFutureValue-shouldneally
方法或KWCaptureSpy
来测试代码,但这在调用嵌套块时可能没有帮助
如果有人有一个更合适的方法来测试像这样的案例,我很高兴听到它 我尝试了几种方法来解决这个问题,但没有一种是正确的
1)将dispatch\u async
移动到自己的类中
+ (void)dispatchOnMainQueue:(Block)block
{
if ([NSThread currentThread] == [NSThread mainThread]) {
block();
} else {
dispatch_sync(dispatch_get_main_queue(), block);
}
}
+ (void)dispatchOnBackgroundQueue:(Block)block
{
dispatch_queue_t background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(background, block);
}
然后,在测试执行期间,将后台调度切换到主队列上。这是可行的,但不可预测。我也觉得很不对
2)将设置代码移动到Kiwi
之前的块,然后休眠主线程。
这是因为Kiwi
测试在主线程上运行,所以我们实际上是在说“在进行测试之前让后台操作发生”。我想这就是我要用的。是的,它使我的单元测试运行得更慢,但是当它们运行时,它们通过了