【问题标题】:XCTAssertThrowsError strange behavior with custom errorHandlerXCTAssertThrowsError 自定义 errorHandler 的奇怪行为
【发布时间】:2016-03-23 22:46:14
【问题描述】:

在我的单元测试中,我有以下代码检查抛出的错误是否属于预期类型。它是通过两个相同的语句完成的,其中一个无法编译:

    enum Error: ErrorType {
        case SomeExpectedError
        case SomeUnexpectedError
    }

    func functionThatThrows() throws {
        throw Error.SomeExpectedError
    }

    // 1) this compiles fine
    XCTAssertThrowsError(try functionThatThrows()) { (error) in
        switch error {
        case Error.SomeExpectedError: break
            //everything is fine
        case Error.SomeUnexpectedError: fallthrough
        default:
            XCTFail("Unexpected error thrown")
        }
    }

    // 2) doesn't compiles at all
    XCTAssertThrowsError(try functionThatThrows()) { (error) in
        XCTAssertEqual(error as? Error, Error.SomeExpectedError)
    }

第一个语句可以编译并且工作正常,但是第二个语句告诉我有两个错误: Errors thrown from here are not handled 和 Cannot convert value of type '(Error) -> Void' to expected argument type 'String'。

这段代码可能有什么问题?错误信息是什么意思?

我正在使用 Xcode 7.3。

【问题讨论】:

    标签: swift error-handling syntax-error closures


    【解决方案1】:

    经过一番研究,我发现我的代码存在 2 个问题:

    1) 似乎我的闭包被认为是"Impilictly returning",因为它只包含一个可执行语句

    2) 编译器被我的陈述弄糊涂了,因为 XCTAssertThows 接受了另外一个接受闭包的参数 - 它是 @autoclosure _ message: () -> String

    结果编译器认为我为消息传递了闭包(恕我直言,设计决策令人困惑 - 将 @autoclosure 作为消息参数传递),而实际上我为 errorHandler 传递了闭包。

    解决方案很简单:

        // explicitly show closure returning type Void so that compiler couldn't
        // use it as message argument (which closure must return String)
        XCTAssertThrowsError(try functionThatThrows()) { (error) -> Void in
            XCTAssertEqual(error as? Error, Error.SomeExpectedError)
        }
    
        // explicitly show closure's argument type
        XCTAssertThrowsError(try functionThatThrows()) { (error: ErrorType) in
            XCTAssertEqual(error as? Error, Error.SomeExpectedError)
        }
    
        // use "message" parameter to preserve argument order
        XCTAssertThrowsError(try functionThatThrows(), "some message") { (error) in
            XCTAssertEqual(error as? Error, Error.SomeExpectedError)
        }
    
        // wrap executable line in "do {}". I guess it works because closure stops
        // being "implicitly returning" and now compiler treat it as () -> Void
        // which is expected type of errorHandler argument
        XCTAssertThrowsError(try functionThatThrows()) { (error) in
            do {
                XCTAssertEqual(error as? Error, Error.SomeExpectedError)
            }
        }
    

    【讨论】:

      【解决方案2】:

      我正在使用这个静态函数来测试特定的异常:

      func XCTAssertThrowsError<T, E: Error & Equatable>(
        _ expression: @autoclosure () throws -> T,
        error: E,
        in file: StaticString = #file,
        line: UInt = #line
        ) {
        var thrownError: Error?
        XCTAssertThrowsError(
          try expression(),
          file: file,
          line: line) {
            thrownError = $0
        }
      
        XCTAssertTrue(
          thrownError is E,
          "Unexpected error type: \(type(of: thrownError))",
          file: file,
          line: line
        )
      
        XCTAssertEqual(
          thrownError as? E,
          error,
          file: file,
          line: line
        )
      }
      

      使用示例:

        func testEmptyMrz() {
          XCTAssertThrowsError(
            try passportMrzValidator.validate(for: ""),
            error: PassportMrzValidationError.invalidMrz
          )
        }
      

      【讨论】:

        猜你喜欢
        • 2014-10-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多