In my unit test I have the following code that checks whether thrown error is of expected type. It is done with two identical statements one of which doesn't compile:
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)
}
First statement compiles and works fine, but seconds one says me that there are two errors:
Errors thrown from here are not handled
and Cannot convert value of type '(Error) -> Void' to expected argument type 'String'
.
What could be wrong with this code? What do error messages mean?
I am using Xcode 7.3 .
After a little research I found 2 problems with my code:
1) seems like my closure considered "Impilictly returning" because it contains only one executable statement
2) compiler was confused by my statement since XCTAssertThows accepts one more parameter that accepts closure - it's @autoclosure _ message: () -> String
As a result compiler thought that I passed closure for message (IMHO confusing design decision - to pass @autoclosure as message argument), when I actually passed closure for errorHandler.
Solutions are simple:
// 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)
}
}