gpt4 book ai didi

objective-c - 如何测试异步方法?

转载 作者:太空狗 更新时间:2023-10-30 03:15:33 24 4
gpt4 key购买 nike

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

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

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

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

最佳答案

想到的三种方式是: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 是一个循环,它处理网络端口、键盘或您插入的任何其他输入源等事件,并在处理完这些事件后或在某个时间限制后返回。当没有事件要处理时,运行循环将线程置于 sleep 状态。所有 Cocoa 和 Core Foundation 应用程序下面都有一个运行循环。您可以在 Apple 的线程编程指南中阅读有关运行循环的更多信息:Run Loops ,或在 Mike Ash Friday Q&A 2010-01-01: NSRunLoop Internals

在这个测试中,我只是使用 NSRunLoop 让线程休眠一秒钟。没有它,while 中的持续循环将消耗 100% 的 CPU 内核。

如果 block 和 bool 标志是在相同的词法范围内创建的(例如:都在一个方法内),那么标志需要 __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");

如果您使用此代码进行单元测试,另一种插入超时的方法是分派(dispatch)带有断言的 block :

// 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");
});

信号量

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

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); 永远等待,我们将被卡住,直到从任务中获得信号,该任务继续在后台队列中运行。

现在假设您必须等待几个区 block 。您可以使用 int 作为标志,或者创建一个以更高数字开头的信号量,或者您可以分组 block 并等待分组完成。在这个例子中,我只用一个 block 来做后面的事情:

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);

如果出于某种原因(为了清理资源?)你想在组完成后运行一个 block ,使用 dispatch_group_notify(group,queue, ^{/*...*/});

关于objective-c - 如何测试异步方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/818674/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com