如何在Swift Testing中使用confirmation()满足expectedCount要求
Swift Testing中confirmation预期计数验证失败问题
我尝试用Swift Testing的confirmation(_:expectedCount:fileID:filePath:line:column:_:)方法的expectedCount参数,验证函数恰好执行25次,但测试报错:
Confirmation was confirmed 0 times, but expected to be confirmed 25 times
我的测试代码如下:
@Test func testStreamingPrimesUpTo100ShouldBe25() async { let maximumCount = 100 await confirmation("Calculate primes up to \(maximumCount)", expectedCount: 25) { primesNumber in PrimeCalculator.calculateStreaming(upTo: maximumCount) { _ in primesNumber() } } }
我曾试过在calculateStreaming的完成闭包里加计数器,用primerNumber.confirm(count: variable)验证,但触发了EXC_BREAKPOINT错误。
目标函数是计算100以内的质数,应该返回25个结果,PrimeCalculator的代码如下:
struct PrimeCalculator { static func calculateStreaming(upTo max: Int, completion: @escaping (Int) -> Void) { DispatchQueue.global().async { guard max > 1 else { return } var sieve = [Bool](repeating: true, count: max) sieve[0] = false sieve[1] = false for number in 2 ..< max { if sieve[number] == true { for multiple in stride(from: number * number, to: sieve.count, by: number) { sieve[multiple] = false } completion(number) } } } } }
以下是XCTest里的等价测试,我想把它迁移到Swift Testing框架:
func testPrimesUpTo100ShouldBe25() { // given let maximumCount = 100 let expectation = XCTestExpectation(description: "Calculate primes up to \(maximumCount)") expectation.expectedFulfillmentCount = 25 // when PrimeCalculator.calculateStreaming(upTo: maximumCount) { number in expectation.fulfill() } wait(for: [expectation], timeout: 3) }
问题分析与解决方法
问题根源
- 异步任务未等待:
calculateStreaming在全局队列异步执行,但测试闭包没有等待该任务完成就退出,导致confirmation根本没机会捕获到任何确认调用,所以报0次确认。 - 错误使用
confirm(count:):这个方法是用来一次性指定确认次数的,不是用来累加计数器的,直接传变量会触发框架的断言断点。
方案1:改造原方法为异步序列(推荐)
将calculateStreaming改为返回AsyncStream,适配Swift并发模型,测试代码更简洁可靠:
// 改造后的PrimeCalculator struct PrimeCalculator { static func streamingPrimes(upTo max: Int) -> AsyncStream<Int> { AsyncStream { continuation in DispatchQueue.global().async { guard max > 1 else { continuation.finish() return } var sieve = [Bool](repeating: true, count: max) sieve[0] = false sieve[1] = false for number in 2 ..< max { if sieve[number] { for multiple in stride(from: number * number, to: sieve.count, by: number) { sieve[multiple] = false } continuation.yield(number) } } continuation.finish() } } } } // 测试代码 @Test func testStreamingPrimesUpTo100ShouldBe25() async { let maximumCount = 100 await confirmation("Calculate primes up to \(maximumCount)", expectedCount: 25) { confirm in for await _ in PrimeCalculator.streamingPrimes(upTo: maximumCount) { confirm() } } }
方案2:不改造原方法,添加等待逻辑
如果不想修改原方法,可以用信号量等待异步任务完成:
@Test func testStreamingPrimesUpTo100ShouldBe25() async { let maximumCount = 100 let completionSemaphore = DispatchSemaphore(value: 0) var primeCount = 0 await confirmation("Calculate primes up to \(maximumCount)", expectedCount: 25) { confirm in PrimeCalculator.calculateStreaming(upTo: maximumCount) { _ in confirm() primeCount += 1 if primeCount == 25 { completionSemaphore.signal() } } completionSemaphore.wait() } }
关键提示
confirmation的闭包必须等待所有异步操作完成,否则测试会提前结束,导致确认计数不足。- 尽量使用Swift原生并发API(如
AsyncStream)替代传统闭包回调,这是Swift Testing框架推荐的异步测试方式。
内容的提问来源于stack exchange,提问作者Javier Heisecke
相关产品推荐
相关产品推荐

