There are several missing features which make your code have to look terrible:
- is case
- An overload of
#expect
which takes a() -> Bool
instead of aBool
. - Swift Testing's lack of incorporation of typed throws.*
@Suite("Test Errors")
struct TestingErrors {
enum ExampleError: Error & Equatable {
case unknownError(_ message: String)
case codedError(_ code:Int)
case noExtras
}
func `throw`(message: String) throws(ExampleError) {
throw .unknownError(message)
}
func `throw`(code: Int) throws(ExampleError) {
throw .codedError(code)
}
func `throw`() throws(ExampleError) {
throw .noExtras
}
@Test func simpleErrorTest() throws {
#expect(throws: ExampleError.noExtras, performing: `throw`)
}
@Test func testForExpectedCode() throws {
let expectedCode = 42
#expect(throws: ExampleError.codedError(expectedCode)) {
try `throw`(code: expectedCode)
}
#expect({
guard case .codedError(let code) = (#expect(throws: ExampleError.self) {
try `throw`(code: expectedCode)
}) else { return false }
return (20...49).contains(code)
} ())
}
@Test func testForAnyCode() throws {
#expect({
guard case .codedError = (#expect(throws: ExampleError.self) {
try `throw`(code: .random(in: 0...5))
}) else { return false }
return true
}() )
}
@Test func testForMessage() throws {
#expect({
guard case .unknownError(let message) = (#expect(throws: ExampleError.self) {
try `throw`(message: "well that was a big oops")
}) else { return false }
return message.contains("oops")
} ())
}
}
* This is the least important; typed throws feel like they're another abandoned feature at this point, and require redundant annotation which is worse than providing a metatype.
expect {
guard case .unknownError(let message) = (expectThrows { () throws(ExampleError) in
try `throw`(message: "well that was a big oops")
}) else { return false }
return message.contains("oops")
}
func expectThrows<Error>(performing expression: () throws(Error) -> some Any) -> Error? {
#expect(throws: Error.self, performing: expression)
}
func expect(_ condition: () -> Bool) {
#expect(condition())
}