我有一个通过网络获取XML或JSON的对象.完成此提取后,它会调用一个选择器,传入返回的数据.所以,例如,我有类似的东西:
-(void)testResponseWas200 { [MyObject get:@"foo.xml" withTarget:self selector:@selector(dataFinishedLoading:)]; }
我尝试在Test类中实现dataFinishedLoading的路径,并尝试在该方法内部进行测试,但测试套件只是锁定.这似乎是嘲弄的一个案例,但我想知道其他人是否遇到过这种情况以及他们是如何处理的.
仅供参考:我正在使用gh-unit进行测试,任何以test*为前缀的方法都会自动执行.
想到的三种方式是: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, ^{/*...*/});