在 XCTFail() 的单元测试中捕获 `throw` 的位置

问题描述 投票:0回答:1

我有一个用于单元测试的辅助函数,它在单独的任务中运行闭包。如果该闭包抛出,我想将错误记录为来自抛出位置的错误,就像 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”断点,但我不知道如何重现它。

swift xctest
1个回答
0
投票

这对我有用,前提是您可以控制引发的错误。

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)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.