This is my method definition:
func isValidForMode(mode: DBFindViewControllerMode) throws -> Bool { }
Now I can test this in simple way, since I know that it DO NOT throws an error:
XCTAssertTrue(try! searchOptionsManager.isValidForMode(.Address))
But what if I know that method throws?
The best solution would be XCTAssertThrows()
, but it is not:-)
Below is my try:
do {
try searchOptionsManager.isValidForMode(.Address)
} catch let error {
XCTAssertEqual(error as! DBErrorType, DBErrorType.CannotBeEmpty("Street"))
}
But it fails, because:
Cannot find an overload for
XCTAssertEqual
that accepts an argument list of type(DBErrorType, DBErrorType)
There are four ways to handle errors in Swift. You can propagate the error from a function to the code that calls that function, handle the error using a do - catch statement, handle the error as an optional value, or assert that the error will not occur.
do – This keyword starts the block of code that contains the method that can potentially throw an error. try – You must use this keyword in front of the method that throws. Think of it like this: “You're trying to execute the method.
According to Swift Error Handling Rationale, the general advice is: Logical error indicates that a programmer has made a mistake. It should be handled by fixing the code, not by recovering from it. Swift offers several APIs for this: assert() , precondition() and fatalError() .
If you know that the function throws an error then you should also ensure that you fail in the event that an error is not thrown.
I'm modifying answers from @robertvojta and @vadim-bulavin here:
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")
}
}
}
Usage:
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)
}
Make your DBError
conforming to 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
}
}
And then you can use it in XCTAssertEqual
:
func testExample() {
do {
try isValid()
}
catch let e as DBError {
XCTAssertEqual(e, DBError.CannotBeEmpty(message: "Street"))
}
catch {
XCTFail("Wrong error")
}
}
Or create your own 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
}
}
And:
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() }
}
}
Or simply use an optional 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)
}
}
No need to conform to Equatable
The best solution so far I have found is:
do {
try searchOptionsManager.isValidForMode(.Address)
XCTAssertTrue(false)
} catch {
XCTAssertTrue(true)
}
This way you can test if exception is really thrown, but you cannot check what type of exception is thrown.
Here is @robertvojta answer with several modification for Xcode 9 and 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")
}
}
}
Usage:
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)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With