Last active
June 30, 2022 14:29
-
-
Save NateFuller/f4a427f97952895e48f79a793a774148 to your computer and use it in GitHub Desktop.
A wrapper for assertion throwing that we can use in Nimble/Quick so it doesn't crash test execution
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
protocol AssertionProvider { | |
func assertionFailure(_ message: String) | |
func fatalError(_ message: String) | |
} | |
final class AssertionHandler { | |
private(set) static var shared: AssertionHandler = AssertionHandler() | |
var provider: AssertionProvider = DefaultAssertionProvider() | |
private init() {} | |
func assertionFailure(_ message: String) { | |
provider.assertionFailure(message) | |
} | |
func fatalError(_ message: String) { | |
provider.fatalError(message) | |
} | |
func resetProvider() { provider = DefaultAssertionProvider() } | |
} | |
/** | |
The default assertion provider. Calls global-level methods for stopping program execution. | |
*/ | |
final class DefaultAssertionProvider: AssertionProvider { | |
func assertionFailure(_ message: String) { | |
Swift.assertionFailure(message) | |
} | |
func fatalError(_ message: String) { | |
Swift.fatalError(message) | |
} | |
} | |
// Below - Usage example from within production code | |
class MyClass { | |
func methodThatFatalErrors() { | |
AssertionHandler.shared.fatalError("Fatal Error") | |
} | |
func methodThatAssertsFailure() { | |
AssertionHandler.shared.assertionFailure("Assertion Failure") | |
} | |
} | |
// Below - defined separately, probably in a module that only is used by your test target(s) | |
import Quick | |
import Nimble | |
@testable import ModuleThatDefines | |
// global, to be used by unit-tests | |
func expectAssertion<T>(_ closure: @escaping () -> T?) { | |
defer { ModuleThatDefines.AssertionHandler.shared.resetProvider() } | |
let assertionProvider = TestAssertionProvider() | |
ModuleThatDefines.AssertionHandler.shared.provider = assertionProvider | |
expect(closure()).to(assertionProvider.throwAssertion()) | |
} | |
func expectAssertion<T>(_ closure: @escaping @autoclosure () -> T?) { | |
expectAssertion(closure) | |
} | |
/** | |
A test-only wrapper that allows us to circumvent Nimble not being able to capture `assertFailure` or `fatalError` | |
without crashing. | |
*/ | |
class TestAssertionProvider: AssertionProvider { | |
private var errorMessage: String? | |
func assertionFailure(_ message: String) { | |
errorMessage = message | |
} | |
func fatalError(_ message: String) { | |
errorMessage = message | |
} | |
// MARK: - TestAssertionProvider specific | |
func throwAssertion<Out>() -> Predicate<Out> { | |
return Predicate { actualExpression in | |
_ = try! actualExpression.evaluate() // swiftlint:disable:this force_try | |
let message = ExpectationMessage.expectedTo("throw an assertion via TestAssertionProvider") | |
return PredicateResult( | |
bool: self.errorMessage != nil, | |
message: message) | |
} | |
} | |
} | |
// Below - Usage example from within unit tests | |
class MyClassSpec: QuickSpec { | |
override func spec() { | |
describe("MyClass") { | |
describe("methodThatFatalErrors") { | |
it("throws a fatal error") { | |
expectAssertion(MyClass().methodThatFatalErrors()) | |
} | |
} | |
describe("methodThatAssertsFailure") { | |
it("asserts failure") { | |
expectAssertion(MyClass().methodThatAssertsFailure()) | |
} | |
} | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment