Skip to content

Instantly share code, notes, and snippets.

@briancroom
Last active August 29, 2015 14:23
Show Gist options
  • Save briancroom/b8a50eed4cff2cbd4abd to your computer and use it in GitHub Desktop.
Save briancroom/b8a50eed4cff2cbd4abd to your computer and use it in GitHub Desktop.
Swift Error Assertions
/**
Response to: http://owensd.io/2015/06/22/catching-errors-for-testing-or-enums-suck.html
After seeing the new features introduced in Xcode 7 beta 2, it seemed that it should be
possible to take David's 4th option one step further. This allows writing assertions
that look exactly as David wishes. The only requirement is that any types used as
associated values on an error enum have to adopt the `DefaultConstructible` protocol
to allow for an instance of the enum case to be created.
**/
import XCTest
///// Error types and test function
enum MyErrors : ErrorType {
case Basic
case MoreInfo(title: String, description: String)
}
struct MyBasicError : ErrorType {
let _code: Int
let _domain: String
}
struct MoreInfoError : ErrorType {
let _code: Int
let _domain: String
let title: String
let description: String
}
func f(value: Int) throws {
switch value {
case 0:
throw MyErrors.Basic
case 1:
throw MyErrors.MoreInfo(title: "A title?", description: "1s are bad, k?")
case 2:
throw MyBasicError(_code: 0, _domain: "my.error")
case 3:
throw MoreInfoError(_code: 0, _domain: "my.error", title: "A title?", description: "3s are bad, k?")
default:
break
}
}
///// Assertions
// Underlying implementations, from David's blog posts
func _XCTAssertThrowsErrorOfType(@autoclosure expression: () throws -> (), type: MirrorType, message: String = "", file: String = __FILE__, line: UInt = __LINE__) {
do {
try expression()
XCTFail(message, file: file, line: line)
}
catch {
if reflect(error).summary != type.summary {
XCTFail(type.summary, file: file, line: line)
}
}
}
func XCTAssertDoesNotThrow(@autoclosure expression: () throws -> (), message: String = "", file: String = __FILE__, line: UInt = __LINE__) {
do {
try expression()
}
catch {
XCTFail(message, file: file, line: line)
}
}
// Form A: For enum cases with no associated values
func XCTAssertThrowsErrorOfType<T where T: ErrorType>(@autoclosure expression: () throws -> (), type: T, message: String = "", file: String = __FILE__, line: UInt = __LINE__) {
_XCTAssertThrowsErrorOfType(expression, type: reflect(type), message: message, file: file, line: line)
}
// Form B: For enum cases with associated values
func XCTAssertThrowsErrorOfType<T: DefaultConstructible, U>(@autoclosure expression: () throws -> (), type: ((T) -> U), message: String = "", file: String = __FILE__, line: UInt = __LINE__) {
_XCTAssertThrowsErrorOfType(expression, type: reflect(type(T())), message: message, file: file, line: line)
}
func XCTAssertThrowsErrorOfType<T: DefaultConstructible, U: DefaultConstructible, V>(@autoclosure expression: () throws -> (), type: ((T, U) -> V), message: String = "", file: String = __FILE__, line: UInt = __LINE__) {
_XCTAssertThrowsErrorOfType(expression, type: reflect(type(T(), U())), message: message, file: file, line: line)
}
// Add implementations with additional arguments as needed...
// Form C: For structs
func XCTAssertThrowsErrorOfType(@autoclosure expression: () throws -> (), type: Any.Type, message: String = "", file: String = __FILE__, line: UInt = __LINE__) {
_XCTAssertThrowsErrorOfType(expression, type: reflect(type), message: message, file: file, line: line)
}
// Required by Form B. Any types used as enum associated values need to adopt this protocol
protocol DefaultConstructible {
init()
}
extension String: DefaultConstructible {}
///// Tests
func testFThrowsOn0() {
// Requires "Form A"
XCTAssertThrowsErrorOfType(try f(0), type: MyErrors.Basic)
}
func testFThrowsOn1() {
// Requires "Form B"
XCTAssertThrowsErrorOfType(try f(1), type: MyErrors.MoreInfo)
}
func testFThrowsOn2() {
// Requires "Form C"
XCTAssertThrowsErrorOfType(try f(2), type: MyBasicError.self)
}
func testFThrowsOn3() {
// Requires "Form C"
XCTAssertThrowsErrorOfType(try f(3), type: MoreInfoError.self)
}
func testFDoesNotThrowOn5() {
XCTAssertDoesNotThrow(try f(5))
}
testFThrowsOn0()
testFThrowsOn1()
testFThrowsOn2()
testFThrowsOn3()
testFDoesNotThrowOn5()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment