问题描述
这与我之前的问题有关,但与我所想的完全不同,因此我将其放入了一个新的库中.我有一些代码在定制队列上异步运行,然后在完成时在主线程上执行完成块.我想围绕这种方法编写单元测试.我在MyObject
上的方法看起来像这样.
This is related to my previous question, but different enough that I figured I'd throw it into a new one. I have some code that runs async on a custom queue, then executes a completion block on the main thread when complete. I'd like to write unit test around this method. My method on MyObject
looks like this.
+ (void)doSomethingAsyncThenRunCompletionBlockOnMainQueue:(void (^)())completionBlock {
dispatch_queue_t customQueue = dispatch_queue_create("com.myObject.myCustomQueue", 0);
dispatch_async(customQueue, ^(void) {
dispatch_queue_t currentQueue = dispatch_get_current_queue();
dispatch_queue_t mainQueue = dispatch_get_main_queue();
if (currentQueue == mainQueue) {
NSLog(@"already on main thread");
completionBlock();
} else {
dispatch_async(mainQueue, ^(void) {
NSLog(@"NOT already on main thread");
completionBlock();
});
}
});
}
我进行了主队列测试以提高安全性,但它总是命中dispatch_async
.我的单元测试如下所示.
I threw in the main queue test for extra safety, but It always hits the dispatch_async
. My unit test looks like the following.
- (void)testDoSomething {
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
void (^completionBlock)(void) = ^(void){
NSLog(@"Completion Block!");
dispatch_semaphore_signal(sema);
};
[MyObject doSomethingAsyncThenRunCompletionBlockOnMainQueue:completionBlock];
// Wait for async code to finish
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
dispatch_release(sema);
STFail(@"I know this will fail, thanks");
}
我创建了一个信号量,以阻止测试在异步代码之前完成.如果我不需要完成块在主线程上运行,这将非常有用.但是,正如几个人在我上面链接的问题中指出的那样,测试在主线程上运行,然后我将完成块排入主线程这一事实意味着我将永远挂死.
I create a semaphore in order to block the test from finishing before the async code does. This would work great if I don't require the completion block to run on the main thread. However, as a couple folks pointed out in the question I linked to above, the fact that the test is running on the main thread and then I enqueue the completion block on the main thread means I'll just hang forever.
从异步队列调用主队列是一种模式,我看到了很多用于更新UI等的模式.有没有人有更好的模式来测试回调到主队列的异步代码?
Calling the main queue from an async queue is a pattern I see a lot for updating the UI and such. Does anyone have a better pattern for testing async code that calls back to the main queue?
推荐答案
有两种方法可以将分配给主队列的块运行.如Drewsmits所述,第一个是通过dispatch_main
.但是,正如他还指出的那样,在测试中使用dispatch_main
存在一个大问题:它永远不会返回.它只会坐在那儿,等待所有可能进入永恒的障碍.可以想象,这对单元测试没有太大帮助.
There are two ways to get blocks dispatched to the main queue to run. The first is via dispatch_main
, as mentioned by Drewsmits. However, as he also noted, there's a big problem with using dispatch_main
in your test: it never returns. It will just sit there waiting to run any blocks that come its way for the rest of eternity. That's not so helpful for a unit test, as you can imagine.
幸运的是,还有另一种选择.在 dispatch_main
手册页,它是这样的:
Luckily, there's another option. In the COMPATIBILITY
section of the dispatch_main
man page, it says this:
换句话说,如果您在Cocoa应用程序中,则主线程的NSRunLoop
耗尽了调度队列.因此,我们需要做的就是在等待测试完成的同时保持运行循环的运行.看起来像这样:
In other words, if you're in a Cocoa app, the dispatch queue is drained by the main thread's NSRunLoop
. So all we need to do is keep the run loop running while we're waiting for the test to finish. It looks like this:
- (void)testDoSomething {
__block BOOL hasCalledBack = NO;
void (^completionBlock)(void) = ^(void){
NSLog(@"Completion Block!");
hasCalledBack = YES;
};
[MyObject doSomethingAsyncThenRunCompletionBlockOnMainQueue:completionBlock];
// Repeatedly process events in the run loop until we see the callback run.
// This code will wait for up to 10 seconds for something to come through
// on the main queue before it times out. If your tests need longer than
// that, bump up the time limit. Giving it a timeout like this means your
// tests won't hang indefinitely.
// -[NSRunLoop runMode:beforeDate:] always processes exactly one event or
// returns after timing out.
NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:10];
while (hasCalledBack == NO && [loopUntil timeIntervalSinceNow] > 0) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:loopUntil];
}
if (!hasCalledBack)
{
STFail(@"I know this will fail, thanks");
}
}
这篇关于单元测试异步队列的模式,该模式在完成时调用主队列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!