如何在XCTest中捕获throw语句位置并关联至XCTFail?
异步闭包抛出错误时,让XCTest定位到实际抛出位置的解决方案
直接用XCTIssue替代XCTFail(),利用Swift错误自带的调用栈信息,就能让Xcode精准定位到闭包内的throw语句,而不是辅助函数里的XCTFail行。
修改后的代码
把辅助函数放到XCTestCase的扩展里(这样能直接访问测试用例的record方法):
extension XCTestCase { func process(_ closure: @escaping @Sendable () async throws -> Void) { Task { [weak self] in do { try await closure() } catch { // 将捕获的错误包装成XCTIssue并记录 let issue = XCTIssue(error: error) self?.record(issue) } } } } final class testTests: XCTestCase { enum TestError: Error { case error } func testThrowsLocatesErrorCorrectly() async throws { let exp = expectation(description: "f() is called") process { defer { exp.fulfill() } print("do things") throw TestError.error // Xcode现在会直接指向这一行 } await fulfillment(of: [exp], timeout: 2) } }
为什么之前的方案没效果?
- 传
line/file给process只能拿到调用process的代码行,追踪不到闭包内部的抛出点; - 异步Task执行时,原始抛出点的栈已经销毁,
Thread.callStackSymbols拿不到有效信息; - 改成非抛出闭包会让测试代码变得啰嗦,丢掉原生
throw的便捷性; XCTFail()默认用自身所在行的位置,而XCTIssue会自动解析错误携带的栈信息,关联到实际抛出错误的代码行。
额外提示
如果你的自定义错误没有自动携带足够的栈信息,可以让它遵循CustomNSError协议,手动补充错误的用户信息,但大多数情况下系统自动捕获的栈信息已经足够Xcode定位。
内容的提问来源于stack exchange,提问作者Rob Napier
相关产品推荐
相关产品推荐

