如何测试在 Swift 2 中抛出错误的方法?

How to test method that throws error in Swift 2?

这是我的方法定义:

func isValidForMode(mode: DBFindViewControllerMode) throws -> Bool { }

现在我可以用简单的方式测试它,因为我知道它不要 抛出错误:

XCTAssertTrue(try! searchOptionsManager.isValidForMode(.Address))

但是如果我知道该方法抛出怎么办?

最好的解决方案是 XCTAssertThrows(),但不是:-)

下面是我的尝试:

do {
    try searchOptionsManager.isValidForMode(.Address)
} catch let error {
    XCTAssertEqual(error as! DBErrorType, DBErrorType.CannotBeEmpty("Street"))
}

但是失败了,因为:

Cannot find an overload for XCTAssertEqual that accepts an argument list of type (DBErrorType, DBErrorType)

到目前为止我找到的最佳解决方案是:

do {
    try searchOptionsManager.isValidForMode(.Address)
    XCTAssertTrue(false)
} catch {
    XCTAssertTrue(true)
}

这种方式可以测试是否真的抛出了异常,但是无法判断抛出的是什么类型的异常。

让你的DBError符合Equatable:

enum DBError: ErrorType, Equatable {
  case CannotBeEmpty(message: String)
}

func ==(lhs: DBError, rhs: DBError) -> Bool {
  switch (lhs, rhs) {
    case (.CannotBeEmpty(let leftMessage), .CannotBeEmpty(let rightMessage)):
      return leftMessage == rightMessage
  }
}

然后就可以在XCTAssertEqual中使用了:

func testExample() {
  do {
    try isValid()
  }
  catch let e as DBError {
    XCTAssertEqual(e, DBError.CannotBeEmpty(message: "Street"))
  }
  catch {
    XCTFail("Wrong error")
  }
}

或创建您自己的 XCTAssertThrows

enum DBError: ErrorType, Equatable {
  case CannotBeEmpty(message: String)
}

func ==(lhs: DBError, rhs: DBError) -> Bool {
  switch (lhs, rhs) {
    case (.CannotBeEmpty(let leftMessage), .CannotBeEmpty(let rightMessage)):
      return leftMessage == rightMessage
  }
}

并且:

func XCTAssertThrows<T: ErrorType where T: Equatable>(error: T, block: () throws -> ()) {
  do {
    try block()
  }
  catch let e as T {
    XCTAssertEqual(e, error)
  }
  catch {
    XCTFail("Wrong error")
  }
}

class TestsTests: XCTestCase {

    func testExample() {
      XCTAssertThrows(DBError.CannotBeEmpty(message: "Street")) { try isValid() }
    }

}

这是您将理解 try-catch 的示例,检查下面的代码

func validateCredencials() throws {

guard username.characters.count > 0  && password.characters.count > 0 
else { throw EncryptionType.Empty }
    guard password.characters.count >= 5 else { throw EncryptionType.Short }
}
    do {
    try validateCredencials()
    }catch EncryptionType.Empty {
        print("password Empty")

    } catch EncryptionType.Short {
        print("Password too shoot")
    }catch {
        print("Some thing went Wrong")
    }

希望你明白

或者简单地使用可选的 try:

extension XCTestCase {
    func XCTAssertThrows(@autoclosure expression: () throws -> Void, _ message: String = "", file: String = __FILE__, line: UInt = __LINE__) {
        XCTAssert((try? expression()) == nil, message, file: file, line: line)
    }
}

不需要符合Equatable

这是@robertvojta 的回答,对 Xcode 9 和 Swift 3 - 4 进行了一些修改:

extension XCTestCase {
    func XCTAssertThrows<ErrorType: Error, T>(expression: @autoclosure () throws -> T, error: ErrorType) where ErrorType: Equatable {
        do {
            _ = try expression()
        } catch let caughtError as ErrorType {
            XCTAssertEqual(caughtError, error)
        } catch {
            XCTFail("Wrong error")
        }
    }
}

用法:

enum APIError: LocalizedError {
    case cancelled

    public var errorDescription: String? {
        switch self {
        case .cancelled:
            return "The operation has been cancelled by user."
        }
    }
}

func testThatIsThrowsCancelledByUserError() {
    XCTAssertThrows(expression: try api.cancelLoginOperation(), error: APIError.cancelled)
}

如果您知道函数会抛出错误,那么您还应该确保在没有抛出错误的情况下失败。

我正在修改来自@robertvojta 和@vadim-bulavin 的答案:

extension XCTestCase {
    func XCTAssertThrows<ErrorType: Error, T>(expression: @autoclosure () throws -> T, error: ErrorType) where ErrorType: Equatable {
        do {
            _ = try expression()
            XCTFail("No error thrown")
        } catch let caughtError as ErrorType {
            XCTAssertEqual(caughtError, error)
        } catch {
            XCTFail("Wrong error")
        }
    }
}

用法:

enum APIError: LocalizedError {
    case cancelled

    public var errorDescription: String? {
        switch self {
        case .cancelled:
            return "The operation has been cancelled by user."
        }
    }
}

func testThatIsThrowsCancelledByUserError() {
    XCTAssertThrows(expression: try api.cancelLoginOperation(), error: APIError.cancelled)
}