单元测试异步队列的模式,该模式在完成时调用主队列 [英] Pattern for unit testing async queue that calls main queue on completion

查看:87
本文介绍了单元测试异步队列的模式,该模式在完成时调用主队列的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这与我之前的问题有关,但与我所想的完全不同,因此我将其放入了一个新的库中.我有一些代码在定制队列上异步运行,然后在完成时在主线程上执行完成块.我想围绕这种方法编写单元测试.我在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:

可可应用程序无需调用dispatch_main().提交给的区块 主队列将作为常规模式"的一部分执行 应用程序的主NSRunLoop或CFRunLoop.

Cocoa applications need not call dispatch_main(). Blocks submitted to the main queue will be executed as part of the "common modes" of the application's main NSRunLoop or CFRunLoop.

换句话说,如果您在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");
    }
}

这篇关于单元测试异步队列的模式,该模式在完成时调用主队列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆