Swift 包 BCTest 提供了在 Swift 测试中断言异步条件的功能,其人体工程学设计与 XCTestExpectation 类似。
@Test @BCTest myTest() async throws {
let myNeed = await needManager.need(expectedCount: 1)
let myOtherNeed = await needManager.need(expectedCount: 1)
Task.detached {
myNeed.satisfy()
myOtherNeed.satisfy()
}
try await awaitSatisfaction(of: [myNeed, myOtherNeed], timeout: .infinity)
}
needManager 是 BCTestNeedManager 的一个实例。@BCTest 宏在 Swift 测试主体的顶部创建 needManager。使用 needManager 来创建 BCTestNeed 的实例。
使用全局函数 awaitSatisfaction(of:timeout:) 等待 BCTestNeed 的满足。
所有已创建的 need 都必须被等待 (awaited) 且被满足 (satisfied),Swift 测试才能通过。@BCTest 宏将 await needManager.assertNeedsSatisfied() 添加到 Swift 测试主体的末尾。
展开后的宏
@Test @BCTest myTest() async throws {
let needManager = BCTestNeedManager() // *** ADDED BY MACRO ***
let myNeed = await needManager.need(expectedCount: 1)
let myOtherNeed = await needManager.need(expectedCount: 1)
Task.detached {
myNeed.satisfy()
myOtherNeed.satisfy()
}
try await awaitSatisfaction(of: [myNeed, myOtherNeed], timeout: .infinity)
await needManager.assertNeedsSatisfied() // *** ADDED BY MACRO ***
}
一个测试 need 被配置为期望零次或多次 satisfy() 的调用。一个测试 need 被配置为在过度满足 (over satisfied) 时是否失败。
一个 BCTestNeed 只能由一个 BCTestNeedManager 创建。
对 need 满足的断言只能由创建它的 need 管理器执行。
如果一个 need 永远不会被满足,则将其配置为 expectedCount: 0。如果在 need 上调用 satisfy()(例如,在不应该执行的代码路径中),该 need 将被过度满足,并且测试将失败。
如果一个测试被设计为 need 管理器的断言应该失败,请包含 .withKnownIssue 特性以避免测试失败,该特性将 need 管理器的断言包装在 withKnownIssue {..} 中。
BCTest 提供了以下不变性
注释:在等待结束后调用 satisfy() 会增加 need 的满足计数,但不能算作满足。
宏展开要求 BCTestNeedManager 的方法 init 和 assertNeedsSatisfied() 是公共的,但是你不应该创建另一个 need 管理器,因为宏不能确保 need 管理器断言满足并等待它创建的 need。
如果一个测试辅助函数创建了 need,请将宏提供的 needManager 传递给它。
建议使用 Confirmation 和 CheckedContinuation 在 Swift 测试中测试异步事件,但它们的人体工程学友好性不如 XCTest 的 XCTestExpectation。
Confirmation 要求确认发生在它的 body 参数退出之前。await confirmation() { c in Task { c() } } 会失败,而 await confirmation() { c in await Task { c() }.value } 会通过。
CheckedContinuation 不需要在其 body 参数退出之前 resume()。
await withCheckedContinuation { c in
Task {
let cookies = await chimChim.threwCookies()
#expect(cookies.count == 1)
c.resume()
}
}
但是 CheckedContinuation 只能 resume() 一次,因此无法在内部跟踪将要发生多次的事件。
此外,CheckedContinuation 需要小心,确保 resume() 在所有 @expect/#require 语句*之后*被调用,否则 Swift 测试上下文可能会错过它们。请参阅 Tests/AlternativeTests 中的 CheckedContinuationShouldFail 和 CheckedContinuationWithKnownIssue 测试。
最后,Confirmation 和 CheckedContinuation 与一个闭包以及闭包内发生的确认/延续具有 1:1 的关系。一个确保多个异步事件发生的测试可能需要嵌套这些结构的实例。
此包在 Unlicensed 许可证下发布。