iOS单元测试:等待期望的时间间隔

时间:2014-09-25 20:41:44

标签: ios unit-testing xcode6

我正在尝试更新我的异步单元测试以使用新的XCTestExpectation接口而不是手动旋转运行循环。

我的单元测试以前使用的函数waitForBlockfinishBlockwaitForTimeInterval:这只是一种在指定时间后调用finishBlock的便捷方法。我正在尝试更新此设置以使用期望值。

使用waitForBlock + finishBlock语义的测试在被waitForExpectationsWithTime:handler:fulfill替换之后都正常工作,但我的替换{{1似乎无法正常工作。

waitForTimeInterval:

编辑:

似乎代码实际上 工作......所以这可能只是Xcode 6今天下午与我联系。


我觉得它应该是相当简单的:创建一个期望,设置一个满足的异步块,然后等待。但是,永远不会调用- (void)waitForTimeInterval:(NSTimeInterval)delay { XCTestExpectation *expectation = [self expectationWithDescription:@"wait"]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [expectation fulfill]; }); [self waitForExpectationsWithTimeout:delay + 1 handler:nil]; } 块。

我的预感是dispatch_after阻塞其当前线程,即主队列,因此运行循环永远不会到达其异步块。这似乎是合理的,但我在提出实现此功能的不同方法时遇到了麻烦。

我正在寻找1)可能揭示变通方法的waitForExpectationsWithTimeout:handler:的其他信息,或2)实现此功能的不同想法。

4 个答案:

答案 0 :(得分:9)

dispatch_async(dispatch_get_main_queue(), ...)似乎在Xcode 7 UI Tests中不起作用,永远不会调用完成处理程序。 Swift中不再提供performSelector,但还有其他两种解决方法:

  1. 使用计时器

    var waitExpectation: XCTestExpectation?
    
    func wait(duration: NSTimeInterval) {
        waitExpectation = expectationWithDescription("wait")
        NSTimer.scheduledTimerWithTimeInterval(duration, target: self,
            selector: Selector("onTimer"), userInfo: nil, repeats: false)
        waitForExpectationsWithTimeout(duration + 3, handler: nil)
    }
    
    func onTimer() {
        waitExpectation?.fulfill()
    }
    
  2. 在全局队列上运行块(它可以工作,但可能不安全,因为它没有记录XCTestExpectation是线程安全的任何地方)。

    func wait(duration: NSTimeInterval) {
        let expectation = expectationWithDescription("wait")
        let dispatchTime = dispatch_time(DISPATCH_TIME_NOW,
            Int64(duration * Double(NSEC_PER_SEC)))
        dispatch_after(dispatchTime,
            dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
            expectation.fulfill()
        }
        waitForExpectationsWithTimeout(duration + 3, handler: nil)
    }
    

答案 1 :(得分:6)

我已在许多项目中使用XCTestExpectation处理各种异步Web和GCD内容...... AFAIK,类似以下内容似乎是最常见的用法:

- (void)testExample {
    // create the expectation with a nice descriptive message
    XCTestExpectation *expectation = [self expectationWithDescription:@"The request should successfully complete within the specific timeframe."];

    // do something asyncronously
    [someObject doAsyncWithCompletionHandler:^(NSInteger yourReturnValue) {
        // now that the async task it done, test whatever states/values you expect to be after this is
        // completed
        NSInteger expectedValue = 42;
        XCTAssert(yourReturnValue == expectedValue, @"The returned value doesn't match the expected value.");

        // fulfill the expectation so the tests can complete
        [expectation fulfill];
    }];

    // wait for the expectations to be called and timeout after some
    // predefined max time limit, failing the test automatically
    NSTimeInterval somePredefinedTimeout = 3;
    [self waitForExpectationsWithTimeout:somePredefinedTimeout handler:nil];
}

答案 2 :(得分:5)

显然使用performSelector:withObject:afterDelay:按预期工作,但我仍然不确定原因。如果有人知道为什么GCD的dispatch_after不起作用,请提供额外的答案,我会接受。目前,此设置似乎按预期工作:

- (void)waitForTimeInterval:(NSTimeInterval)delay
{
    XCTestExpectation *expectation = [self expectationWithDescription:@"wait"];
    [self performSelector:@selector(fulfillExpectation:) withObject:expectation afterDelay:delay];

    [self waitForExpectationsWithTimeout:delay + 1 handler:nil];
}

- (void)fulfillExpectation:(XCTestExpectation *)expectation
{
    [expectation fulfill];
}

答案 3 :(得分:2)

在我的一个单元测试用例中,我需要在主应用程序代码中测试一个方法的运行,这应该在大约1秒内触发一个计时器来调用应用程序中的另一个方法。在检查结果之前,我使用XCTestExpectation等待和DispatchQueue.asyncAfter作为停止和等待的机制。以下代码是Swift 3/4中的代码段:

    <call the main app method which will trigger a timer event>

    // wait
    let expectation = XCTestExpectation(description: "test")
    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
        expectation.fulfill()
    }
    wait(for: [expectation], timeout: 2.5)

    <check the result of the timer event>