我有一个用于单元测试的辅助函数,它在单独的任务中运行闭包。如果该闭包抛出,我想将错误记录为来自抛出位置的错误,就像 Xcode 对
throws
测试用例所做的那样。为此,我可以将一行和文件传递给 XCTFail
,但我还没有找到任何方法来找到该位置。
func process(_ closure: @escaping @Sendable () async throws -> Void) {
Task {
do {
try await closure()
} catch {
XCTFail() // <--- How I can pass the correct line/file here?
}
}
}
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 // <--- I want Xcode to point here as the error.
}
await fulfillment(of: [exp], timeout: 2)
}
}
完整版本的
process
位于URLProtocol
内部,因此它不能返回任何内容(在startLoading()
中称为)。而且它位于一个单独的文件中,因此在单元测试中 Xcode 中根本不会显示任何消息,因为 XCTFail
位于单元测试文件内。
Thread.callStackSymbols
没有帮助,因为抛出不再在堆栈中。
我可以使
closure
不抛出,并强制人们在每个 XCTAssertNoThrow
上使用 try
,但这使单元测试变得尴尬,失去了“只是抛出”的好处。例如,像 let x = try XCTUnwrap(...)
这样的好工具会变成 let x = XCTAssertNoThrow(try XCTUnwrap(...))
。
我尝试过删除异步,并以这种方式重写
process
,但它没有改善任何东西:
func process(_ closure: @escaping @Sendable () throws -> Void) {
XCTAssertNoThrow(try closure()) // Still puts the error here rather than the throw.
}
我可以将位置传递给
process
,但这只会标记调用 process
的行,而不是抛出异常的行。关闭时间可能会很长。
我添加了一个
XCTestObservation
并检查了XCTIssue
,但除了XCTFail
线之外似乎没有任何东西。
Xcode 本身以某种方式实现了这一点,但我无法对他们正在做的事情进行逆向工程。也许类似于“Swift Error”断点,但我不知道如何重现它。
这对我有用,前提是您可以控制引发的错误。
enum TestError: Error { case error(file: StaticString, line: UInt) }
func process(_ closure: @escaping @Sendable () async throws -> Void) {
Task {
do {
try await closure()
} catch let TestError.error(file, line) {
XCTFail(file: file, line: line) // <--- How I can pass the correct line/file here?
}
}
}
final class testTests: XCTestCase {
func testThrowsLocatesErrorCorrectly() async throws {
let exp = expectation(description: "f() is called")
process {
defer { exp.fulfill() }
print("do things")
throw TestError.error(file: #filePath, line: #line) // <--- I want Xcode to point here as the error.
}
await fulfillment(of: [exp], timeout: 2)
}
}