当前位置:  开发笔记 > 前端 > 正文

你如何测试异步方法?

如何解决《你如何测试异步方法?》经验,为你挑选了1个好方法。

我有一个通过网络获取XML或JSON的对象.完成此提取后,它会调用一个选择器,传入返回的数据.所以,例如,我有类似的东西:

-(void)testResponseWas200
{
    [MyObject get:@"foo.xml" withTarget:self selector:@selector(dataFinishedLoading:)];  
}

我尝试在Test类中实现dataFinishedLoading的路径,并尝试在该方法内部进行测试,但测试套件只是锁定.这似乎是嘲弄的一个案例,但我想知道其他人是否遇到过这种情况以及他们是如何处理的.

仅供参考:我正在使用gh-unit进行测试,任何以test*为前缀的方法都会自动执行.



1> Jano..:

想到的三种方式是:NSRunLoop,信号量和组.

NSRunLoop

__block bool finished = false;

// For testing purposes we create this asynchronous task 
// that starts after 3 seconds and takes 1 second to execute.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);
dispatch_time_t threeSeconds = dispatch_time(DISPATCH_TIME_NOW, 3LL * NSEC_PER_SEC);
dispatch_after(threeSeconds, queue, ^{ 
    sleep(1); // replace this with your task
    finished = true; 
});

// loop until the flag is set from inside the task
while (!finished) {
    // spend 1 second processing events on each loop
    NSDate *oneSecond = [NSDate dateWithTimeIntervalSinceNow:1];
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:oneSecond];
}

NSRunLoop是一个循环,它处理诸如网络端口,键盘或您插入的任何其他输入源之类的事件,并在处理这些事件后或在时间限制之后返回.当没有要处理的事件时,运行循环使线程进入休眠状态.所有Cocoa和Core Foundation应用程序都有一个运行循环.您可以在Apple的" 线程编程指南:运行循环 "或Mike Ash Friday Q&A 2010-01-01:NSRunLoop Internals中阅读有关运行循环的更多信息.

在这个测试中,我只是使用NSRunLoop来休眠线程一秒钟.没有它,它中的常量循环while将消耗100%的CPU核心.

如果块和布尔标志是在相同的词法范围内创建的(例如:两者都在方法内),那么该标志需要__block存储限定符是可变的.如果该标志是一个全局变量,它就不需要它.

如果测试在设置标志之前崩溃,则线程将永远等待.添加时间限制以避免:

NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:2];
while (!finished && [timeout timeIntervalSinceNow]>0) {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode 
                             beforeDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}
if (!finished) NSLog(@"test failed with timeout");

如果您使用此代码进行单元测试,则插入超时的另一种方法是使用断言调度块:

// taken from https://github.com/JaviSoto/JSBarrierOperationQueue/blob/master/JSBarrierOperationQueueTests/JSBarrierOperationQueueTests.m#L118
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, 2LL * NSEC_PER_SEC);
dispatch_after(timeout, dispatch_get_main_queue(), ^(void){
    STAssertTrue(done, @"Should have finished by now");
});

信号

类似的想法,但睡觉,直到信号量改变,或直到时间限制:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

// signal the semaphore after 3 seconds using a global queue
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3LL*NSEC_PER_SEC), queue, ^{ 
    sleep(1);
    dispatch_semaphore_signal(semaphore);
});

// wait with a time limit of 5 seconds
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, 5LL*NSEC_PER_SEC);
if (dispatch_semaphore_wait(semaphore, timeout)==0) {
    NSLog(@"success, semaphore signaled in time");
} else {
    NSLog(@"failure, semaphore didn't signal in time");
}

dispatch_release(semaphore);

相反,如果我们永远等待,dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);我们将被卡住,直到从任务中获取信号,该信号一直在后台队列上运行.

现在想象你必须等待几个街区.您可以使用int作为标志,或创建以更高编号开头的信号量,或者您可以对块进行分组并等待组完成.在这个例子中,我只用一个块做后者:

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);

// dispatch work to the given group and queue
dispatch_group_async(group,queue,^{
    sleep(1); // replace this with your task
});

// wait two seconds for the group to finish
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, 2LL*NSEC_PER_SEC);
if (dispatch_group_wait(group, timeout)==0) {
    NSLog(@"success, dispatch group completed in time");
} else {
    NSLog(@"failure, dispatch group did not complete in time");
}

dispatch_release(group);

如果出于某种原因(要清理资源?),您希望在组完成后运行一个块,请使用 dispatch_group_notify(group,queue, ^{/*...*/});

推荐阅读
手机用户2402851155
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有