我有一个通过网络获取XML或JSON的对象。一旦获取完成,它就会调用选择器,传入返回的数据。所以,举个例子,我的代码是这样的:
-(void)testResponseWas200
{
[MyObject get:@"foo.xml" withTarget:self selector:@selector(dataFinishedLoading:)];
}我尝试了在测试类中实现dataFinishedLoading的路线,并尝试在该方法中进行测试,但测试套件就是锁定了。这看起来像是一个嘲笑的案例,但我想知道其他人是否遇到过这种情况,以及他们是如何处理的。
仅供参考:我使用gh-unit进行测试,任何以test*为前缀的方法都会自动执行。
发布于 2012-06-25 01:13:46
出现在脑海中的三种方式是: 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是一个循环,它处理网络端口、键盘或您插入的任何其他输入源等事件,并在处理这些事件后或在一段时间限制后返回。当没有要处理的事件时,run循环使线程进入休眠状态。所有Cocoa和Core Foundation应用程序都有一个运行循环。你可以在苹果的线程编程指南:或Mike Ash 中阅读更多关于run循环的内容。
在这个测试中,我只是使用NSRunLoop让线程休眠一秒钟。如果没有它,while中的常量循环将消耗100%的CPU核心。
如果块和布尔标志是在相同的词法作用域中创建的(例如:都在一个方法中),那么该标志需要__block storage qualifier是可变的。如果标志是一个全局变量,它就不需要它了。
如果在设置标志之前测试崩溃,线程将永远等待。添加一个时间限制来避免这种情况:
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, ^{/*...*/});
发布于 2009-05-18 07:29:04
发布于 2013-12-17 23:58:32
@jano谢谢你我从你的帖子里做了这个小工具
在PYTestsUtils.m中
+ (void)waitForBOOL:(BOOL*)finished forSeconds:(int)seconds {
NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:seconds];
while (!*finished && [timeout timeIntervalSinceNow]>0) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}
}在我的测试文件
- (void)testSynchronizeTime
{
__block BOOL finished = NO;
[self.connection synchronizeTimeWithSuccessHandler:^(NSTimeInterval serverTime) {
NSLog(@"ServerTime %f", serverTime);
finished = YES;
} errorHandler:^(NSError *error) {
STFail(@"Cannot get ServerTime %@", error);
finished = YES;
}];
[PYTestsUtils waitForBOOL:&finished forSeconds:10];
if (! finished)
STFail(@"Cannot get ServerTime within 10 seconds");
}变化
添加PYTestsUtils.m
+ (void)execute:(PYTestExecutionBlock)block ifNotTrue:(BOOL*)finished afterSeconds:(int)seconds {
[self waitForBOOL:finished forSeconds:seconds];
if (! *finished) block();
}用法:
- (void)testSynchronizeTime
{
__block BOOL finished = NO;
[self.connection synchronizeTimeWithSuccessHandler:^(NSTimeInterval serverTime) {
NSLog(@"ServerTime %f", serverTime);
finished = YES;
} errorHandler:^(NSError *error) {
STFail(@"Cannot get ServerTime %@", error);
finished = YES;
}];
[PYTestsUtils execute:^{
STFail(@"Cannot get ServerTime within 10 seconds");
} ifNotTrue:&finished afterSeconds:10];
}https://stackoverflow.com/questions/818674
复制相似问题