【问题标题】:How to test method that throws error in Swift 2?如何测试在 Swift 2 中引发错误的方法?
【发布时间】:2015-07-22 09:04:13
【问题描述】:

这是我的方法定义:

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

现在我可以用简单的方式来测试它,因为我知道它DO NOT会引发错误:

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

但如果我知道该方法会抛出异常怎么办?

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

下面是我的尝试:

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

但它失败了,因为:

找不到接受 (DBErrorType, DBErrorType) 类型参数列表的 XCTAssertEqual 的重载

【问题讨论】:

    标签: unit-testing error-handling swift2


    【解决方案1】:

    使您的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() }
        }
    
    }
    

    【讨论】:

      【解决方案2】:

      或者干脆使用可选的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

      【讨论】:

        【解决方案3】:

        目前我发现的最佳解决方案是:

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

        这样你可以测试是否真的抛出了异常,但是你无法检查抛出的是什么类型的异常。

        【讨论】:

        • try 不会抛出异常,而是抛出错误。 do catch 不处理异常,而是处理错误。这些也不例外。
        • 我完全同意,但如果 Apple 承认这一点就好了!当 Foundation 中的方法因 failed: caught "NSInvalidArgumentException" 之类的错误而失败时,人们会感到困惑,这是可以理解的,这对我来说听起来像是一个异常。
        【解决方案4】:

        这是@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)
        }
        

        【讨论】:

          【解决方案5】:

          如果您知道函数会引发错误,那么您还应该确保在未引发错误的情况下失败。

          我正在修改来自@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)
          }
          

          【讨论】:

          • 我同意,不错的收获
          【解决方案6】:

          这是你将理解的示例 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")
              }
          

          希望你理解

          【讨论】:

            猜你喜欢
            • 2020-02-26
            • 1970-01-01
            • 2016-05-16
            • 1970-01-01
            • 2019-04-06
            • 2015-11-18
            • 1970-01-01
            • 1970-01-01
            • 2017-03-06
            相关资源
            最近更新 更多