我正在尝试更新我的异步单元测试以使用新的XCTestExpectation
接口而不是手动旋转运行循环。
我的单元测试以前使用的函数waitForBlock
,finishBlock
和waitForTimeInterval:
这只是一种在指定时间后调用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)实现此功能的不同想法。
答案 0 :(得分:9)
dispatch_async(dispatch_get_main_queue(), ...)
似乎在Xcode 7 UI Tests中不起作用,永远不会调用完成处理程序。 Swift中不再提供performSelector
,但还有其他两种解决方法:
使用计时器
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()
}
在全局队列上运行块(它可以工作,但可能不安全,因为它没有记录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>