在iOS中测试单元测试中的异步调用

Bab*_*shi 12 unit-testing ios

我在iOS中单元测试异步调用时遇到问题.(虽然它在视图控制器中工作正常.)

以前有人遇到过这个问题吗?我尝试过使用等待功能,但我仍然遇到同样的问题.

请提供一个很好的方法来举例说明.

Jac*_*kin 28

你需要旋转runloop直到你的回调被调用.但要确保它在主队列上被调用.

试试这个:

__block BOOL done = NO;
doSomethingAsynchronouslyWithBlock(^{
    done = YES;
});

while(!done) {
   [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
Run Code Online (Sandbox Code Playgroud)

您也可以使用信号量(下面的示例),但我更喜欢旋转runloop以允许处理分派到主队列的异步块.

dispatch_semaphore_t sem = dispatch_semaphore_create(0);
doSomethingAsynchronouslyWithBlock(^{
    //...
    dispatch_semaphore_signal(sem);
});

dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
Run Code Online (Sandbox Code Playgroud)


sky*_*der 13

以下是Apple对异步测试的本机支持的描述.

TL; DR手册:

看着 XCTextCase+AsynchronousTesting.h

XCTestExpectation一个只有一个公共方法的特殊类:- (void)fulfill;

您应该初始化此类的实例并在成功案例中调用fulfill方法.否则,您在该方法中指定的超时后测试将失败:

- (void)waitForExpectationsWithTimeout:(NSTimeInterval)timeout handler:(XCWaitCompletionHandler)handlerOrNil;
Run Code Online (Sandbox Code Playgroud)

例:

- (void)testAsyncMethod
{

    //Expectation
    XCTestExpectation *expectation = [self expectationWithDescription:@"Testing Async Method Works Correctly!"];

    [MyClass asyncMethodWithCompletionBlock:^(NSError *error) {        
        if(error)
            NSLog(@"error is: %@", error);
        else
            [expectation fulfill];
    }];

    //Wait 1 second for fulfill method called, otherwise fail:    
    [self waitForExpectationsWithTimeout:1 handler:^(NSError *error) {

        if(error)
        {
            XCTFail(@"Expectation Failed with error: %@", error);
        }

    }];
}
Run Code Online (Sandbox Code Playgroud)


Sor*_*ren 8

我认为这篇文章中的许多建议解决方案存在的问题是,如果异步操作没有完成,则永远不会设置"完成"标志,并且测试将永久挂起.

我在很多测试中都成功地使用了这种方法.

- (void)testSomething {
    __block BOOL done = NO;

    [obj asyncMethodUnderTestWithCompletionBlock:^{
        done = YES;
    }];

    XCTAssertTrue([self waitFor:&done timeout:2],
                   @"Timed out waiting for response asynch method completion");
}


- (BOOL)waitFor:(BOOL *)flag timeout:(NSTimeInterval)timeoutSecs {
    NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeoutSecs];

    do {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeoutDate];
        if ([timeoutDate timeIntervalSinceNow] < 0.0) {
            break;
        }
    }
    while (!*flag);
    return *flag;
}
Run Code Online (Sandbox Code Playgroud)


小智 5

从Xcode 6开始,它作为一个类别内置于XCTest:

请参阅/sf/answers/1729369841/


dea*_*rse 0

尝试 KIWI 框架。它功能强大,可能会帮助您进行其他类型的测试。