Cag*_*ged 17 cocoa unit-testing objective-c
我有一个通过网络获取XML或JSON的对象.完成此提取后,它会调用一个选择器,传入返回的数据.所以,例如,我有类似的东西:
-(void)testResponseWas200
{
[MyObject get:@"foo.xml" withTarget:self selector:@selector(dataFinishedLoading:)];
}
Run Code Online (Sandbox Code Playgroud)
我尝试在Test类中实现dataFinishedLoading的路径,并尝试在该方法内部进行测试,但测试套件只是锁定.这似乎是嘲弄的一个案例,但我想知道其他人是否遇到过这种情况以及他们是如何处理的.
仅供参考:我正在使用gh-unit进行测试,任何以test*为前缀的方法都会自动执行.
Jan*_*ano 30
想到的三种方式是: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];
}
Run Code Online (Sandbox Code Playgroud)
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");
Run Code Online (Sandbox Code Playgroud)
如果您使用此代码进行单元测试,则插入超时的另一种方法是使用断言调度块:
// 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");
});
Run Code Online (Sandbox Code Playgroud)
类似的想法,但睡觉,直到信号量改变,或直到时间限制:
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);
Run Code Online (Sandbox Code Playgroud)
相反,如果我们永远等待,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);
Run Code Online (Sandbox Code Playgroud)
如果出于某种原因(要清理资源?),您希望在组完成后运行一个块,请使用 dispatch_group_notify(group,queue, ^{/*...*/});
测试异步和多线程代码的最佳方法之一是使用事件日志记录。您的代码应该在有趣或有用的时间记录事件。通常,一个事件本身就足以证明逻辑正确运行。有时事件需要有效负载或其他元信息,以便它们可以配对或链接。
当运行时或操作系统支持高效且强大的事件机制时,这是最有用的。这使您的产品能够在“零售”版本中随事件一起发货。在这种情况下,仅当您需要调试问题或运行单元测试以证明 Thins 正常工作时才会启用事件。
在零售(生产)代码中包含事件可以让您在任何平台上进行测试和调试。这比调试或“检查”代码有巨大的好处。
请注意,与断言一样,要小心放置事件的位置 - 如果经常记录它们可能会很昂贵。但好消息是,现代操作系统和一些应用程序框架支持事件机制,可以轻松支持数十个或数千个事件。有些支持对选定事件进行堆栈跟踪。这可能非常强大,但通常要求符号在某个时间点可用 - 无论是在记录时,还是在目标系统上跟踪后处理时。
归档时间: |
|
查看次数: |
4232 次 |
最近记录: |