162 lines
5.0 KiB
Swift
162 lines
5.0 KiB
Swift
//
|
|
// CatchingFire.swift
|
|
// CatchingFire
|
|
//
|
|
// Created by Marius Rackwitz on 7.7.15.
|
|
// Copyright © 2015 Marius Rackwitz. All rights reserved.
|
|
//
|
|
// Copied from https://github.com/mrackwitz/CatchingFire/blob/master/src/CatchingFire.swift
|
|
|
|
import XCTest
|
|
|
|
/// This allows you to write safe tests for the happy path of failable functions.
|
|
/// It helps you to avoid the `try!` operator in tests.
|
|
///
|
|
/// If you want to test a function, which may fail in general, you may think of using `try`.
|
|
/// But this would mean that you have to declare your test method as throwing, which causes that
|
|
/// XCTest doesn't execute the test anymore.
|
|
///
|
|
/// So in consequence, you would usually need to write:
|
|
///
|
|
/// XCTAssertEqual(try! fib(x), 21)
|
|
///
|
|
/// If the expression fails, your whole test suite doesn't execute further and aborts immediately,
|
|
/// which is very undesirable, especially on CI, but also for your workflow when you use TDD.
|
|
///
|
|
/// Instead you can write now:
|
|
///
|
|
/// AssertNoThrow {
|
|
/// XCTAssertEqual(try fib(x), 21)
|
|
/// }
|
|
///
|
|
/// Or alternatively:
|
|
///
|
|
/// AssertNoThrow(try fib(x)).map { (y: Int) in
|
|
/// XCTAssertEqual(y, 21)
|
|
/// }
|
|
///
|
|
/// If the expression fails, your test fails.
|
|
///
|
|
@discardableResult public func AssertNoThrow<R>(_ closure: @autoclosure() throws -> R) -> R? {
|
|
var result: R?
|
|
AssertNoThrow() {
|
|
result = try closure()
|
|
}
|
|
return result
|
|
}
|
|
|
|
@discardableResult public func AssertNoThrow(_ closure: () throws -> ()) {
|
|
do {
|
|
try closure()
|
|
} catch let error {
|
|
XCTFail("Caught unexpected error <\(error)>.")
|
|
}
|
|
}
|
|
|
|
|
|
/// This allows to easily write exhaustive tests for the exception paths of failable functions.
|
|
/// It helps you to avoid writing the same boilerplate code over and over again for tests.
|
|
///
|
|
/// If you want to test a function, that it fails for given arguments, you would usually need
|
|
/// to write:
|
|
///
|
|
/// do {
|
|
/// try fib(-1)
|
|
/// XCTFail("Expected to fail, but did not failed!")
|
|
/// } catch Error.ArgumentMayNotBeNegative {
|
|
/// // succeed silently
|
|
/// } catch error {
|
|
/// XCTFail("Failed with a different error than expected!")
|
|
/// }
|
|
///
|
|
/// Instead you can write now:
|
|
///
|
|
/// AssertThrows(Error.ArgumentMayNotBeNegative) {
|
|
/// try fib(-1)
|
|
/// }
|
|
///
|
|
/// If the expression or closure doesn't throw the expected error, your test fails.
|
|
///
|
|
/*
|
|
public func AssertThrows<R, E where E: ErrorProtocol>(_ expectedError: E, _ closure: @autoclosure(escaping)() throws -> R) -> () {
|
|
AssertThrows(expectedError) { try closure() }
|
|
}
|
|
*/
|
|
@discardableResult public func AssertThrows<E>(_ expectedError: E, _ closure: () throws -> ()) -> () where E: Error {
|
|
do {
|
|
try closure()
|
|
XCTFail("Expected to catch <\(expectedError)>, "
|
|
+ "but no error was thrown.")
|
|
} catch expectedError {
|
|
return // that's what we expected
|
|
} catch {
|
|
XCTFail("Caught error <\(error)>, "
|
|
+ "but not of the expected type and value "
|
|
+ "<\(expectedError)>.")
|
|
}
|
|
}
|
|
/*
|
|
public func AssertThrows<R, E where E: ErrorProtocol, E: Equatable>(_ expectedError: E, _ closure: @autoclosure(escaping)() throws -> R) -> () {
|
|
AssertThrows(expectedError) { try closure() }
|
|
}
|
|
*/
|
|
@discardableResult public func AssertThrows<E>(_ expectedError: E, _ closure: () throws -> ()) -> () where E: Error, E: Equatable {
|
|
do {
|
|
try closure()
|
|
XCTFail("Expected to catch <\(expectedError)>, "
|
|
+ "but no error was thrown.")
|
|
} catch let error as E {
|
|
XCTAssertEqual(error, expectedError,
|
|
"Caught error <\(error)> is of the expected type <\(E.self)>, "
|
|
+ "but not the expected case <\(expectedError)>.")
|
|
} catch {
|
|
XCTFail("Caught error <\(error)>, "
|
|
+ "but not of the expected type and value "
|
|
+ "<\(expectedError)>.")
|
|
}
|
|
}
|
|
|
|
/// Implement pattern matching for ErrorProtocol
|
|
internal func ~=(lhs: Error, rhs: Error) -> Bool {
|
|
return lhs._domain == rhs._domain
|
|
&& rhs._code == rhs._code
|
|
}
|
|
/*
|
|
/// Helper struct to catch errors thrown by others, which aren't publicly exposed.
|
|
///
|
|
/// Note:
|
|
/// Don't use this when a given ErrorProtocol implementation exists.
|
|
/// If you want to use that to test errors thrown in your own framework, you should
|
|
/// consider to adopt an enumeration-based approach first in the framework code itself and expose
|
|
/// them instead as part of the public API. Even if you have some internal error cases, you can
|
|
//// put them in a separate enum and import your framework as `@testable` in your tests without
|
|
/// affecting the public API, if that matters.
|
|
///
|
|
public struct Error : Error {
|
|
public let domain: String
|
|
public let code: Int
|
|
|
|
public var _domain: String {
|
|
return domain
|
|
}
|
|
public var _code: Int {
|
|
return code
|
|
}
|
|
}
|
|
|
|
/// Extend our Error type to conform `Equatable`.
|
|
extension Error : Equatable {}
|
|
|
|
/// Implement the `==` operator as required by protocol `Equatable`.
|
|
public func ==(lhs: Error, rhs: Error) -> Bool {
|
|
return lhs._domain == rhs._domain
|
|
&& lhs._code == rhs._code
|
|
}
|
|
|
|
/// Implement pattern matching for Error & ErrorProtocol
|
|
public func ~=(lhs: Error, rhs: Error) -> Bool {
|
|
return lhs._domain == rhs._domain
|
|
&& rhs._code == rhs._code
|
|
}
|
|
*/
|