ios - Swift:使用抛出异常的函数进行单元测试

标签 ios swift macos unit-testing

如何在 Swift 中编写单元测试,以便在调用可能抛出异常的函数时传达有用的信息?

我真正希望能够做的是这样的:

class TestTestsTests: XCTestCase {

    func doFoo() throws -> String {
        // A complex operation that might throw in various places
        return "foo"
    }

    func doBar() throws -> String {
        // A complex operation that might throw in various places
        return "bar"
    }

    func testExample() throws {
        let foo = try doFoo()
        let bar = try doBar()
        XCTAssertNotEqual(foo, bar)
    }

}

理想情况下,单元测试运行程序将停止在发生未处理异常的行上,并让用户探索堆栈和错误消息。不幸的是,添加throws到测试函数会导致它被默默地跳过(并且有一个 UI 错误,使得测试看起来好像仍在运行,并且得到与添加 throws 之前相同的结果)。

当然也可以这样做:

func testExample() {
    let foo = try! doFoo()
    let bar = try! doBar()
    XCTAssertNotEqual(foo, bar)
}

但是现在失败并不能真正提供我们需要的上下文。添加throwdoFoo ,我们收到类似 fatal error: 'try!' expression unexpectedly raised an error: TestTestsTests.Error(): file /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-700.1.101.15/src/swift/stdlib/public/core/ErrorType.swift, line 50 的消息,它只给我们 testExample 范围内的行号,并且不在 doFoo 范围内发生错误的地方。无论是否启用断点,这似乎也会使调试器“卡住”(单击“继续”只会返回到具有相同错误消息的同一行),并阻止其他测试运行。

那么也许我们可以尝试这样的事情?

func testExample() {
    do {
        let foo = try doFoo()
        let bar = try doBar()
        XCTAssertNotEqual(foo, bar)
    } catch {
        XCTFail("\(error)")
    }
}

这按预期运行,但是我们无法确定 doFoo 中的哪一个或doBar抛出错误,也没有行号信息。至少我们可以得到错误消息并且不阻止其他测试的运行。

我可以继续,但缺点是我找不到一种方法来同时不中断单元测试的运行(如 try! ),找出哪个函数引发了错误,并得到错误信息——除非我做了一些荒谬的事情,比如:

func testExample() {
    var foo: String? = nil
    var bar: String? = nil
    do {
        foo = try doFoo()
    } catch {
        XCTFail("\(error)")
        return
    }
    do {
        bar = try doBar()
    } catch {
        XCTFail("\(error)")
        return
    }

    if let foo = foo, bar = bar {
        XCTAssertNotEqual(foo, bar)
    }
}

我仍然不知道doFoo在哪里或doBar发生错误。

这只是 Swift 单元测试的悲惨状态,还是我错过了什么?

最佳答案

This runs as expected, however we can't determine which of doFoo or doBar threw the error, and also no line number information.

也许这只是一个老问题,或者我不理解你的问题,或者你只是在发表声明而不是提出问题。但对于上面引用的声明,您可以将任何您喜欢的信息添加到 XCTest 断言中的消息中。 Swift 还提供了以下文字:

#file          String   Name of the file where this literal appears.
#line          Int      The line number on which it appears.
#column        Int      The column number in which it begins.
#function      String   The name of the declaration in which it appears. 


func testExample() {
    var foo: String? = nil
    var bar: String? = nil
    do {
        foo = try doFoo()
    } catch {
        XCTFail("try doFoo() failed on line: \(#line) in file: \(#file) with error: \(error)")
        return
    }
    do {
        bar = try doBar()
    } catch {
        XCTFail("try doBar() failed on line: \(#line) in file: \(#file) with error: \(error)")
        return
    }

    if let foo = foo, bar = bar {
        XCTAssertNotEqual(foo, bar)
    }
}

如果您真的想疯狂地使用它,您可以向 doBar() 方法添加错误处理,并且该错误可以包含您想要的任何内部信息。 事实上......通过在方法中实现您自己的错误,您甚至可能不需要在测试中将方法分成两个 block ,只需打印错误就足够了。您可以在错误消息中添加任何您喜欢的信息。

不管怎样,我认为这是一个过时的问题,你可以从测试日志中获取你需要的所有信息 - 它们列出了所有失败的方法,甚至有小箭头让你直接跳到失败的测试。然后他们强调失败的具体断言……从那里很容易看出大多数情况下发生了什么。最坏的情况是您必须设置一两个断点并再次运行测试。

关于ios - Swift:使用抛出异常的函数进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35324505/

相关文章:

macos - 请求 EventKit 访问 macOS SwiftUI

ruby - gem eventmachine fatal error : 'openssl/ssl.h' file not found

ruby-on-rails - 在 OS X 上调试 Ruby 2.1 的方法

ios - 从 Swift 函数中的异步调用返回数据

ios - 应用程序终止时跟踪用户位置

ios - 使用 URL Scheme 打开 IOS 应用程序并传递注册数据

ios - 字典的 Swift 扩展。错误 'Cannot invoke ' dataWithJSONObject'

ios - 在 TabbarViewController 中隐藏特定 View Controller 中的状态栏

ios - 关闭 UIViewController 时重新加载 TableView?

swift - 二元运算符 '>=' 不能应用于类型 ' (UnsafePointer<Int8>,Int32) -> UnsafeMutablePointer<Int8>' 和 'Int' 的操作数