How to implement unit test for a fatalError
code path in Swift?
For example, I\'ve the following swift code
func divide(x: Float, by y:
Based on Ken's answer.
In your App Target add the following:
import Foundation
// overrides Swift global `fatalError`
public func fatalError(_ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) -> Never {
FatalErrorUtil.fatalErrorClosure(message(), file, line)
unreachable()
}
/// This is a `noreturn` function that pauses forever
public func unreachable() -> Never {
repeat {
RunLoop.current.run()
} while (true)
}
/// Utility functions that can replace and restore the `fatalError` global function.
public struct FatalErrorUtil {
// Called by the custom implementation of `fatalError`.
static var fatalErrorClosure: (String, StaticString, UInt) -> Never = defaultFatalErrorClosure
// backup of the original Swift `fatalError`
private static let defaultFatalErrorClosure = { Swift.fatalError($0, file: $1, line: $2) }
/// Replace the `fatalError` global function with something else.
public static func replaceFatalError(closure: @escaping (String, StaticString, UInt) -> Never) {
fatalErrorClosure = closure
}
/// Restore the `fatalError` global function back to the original Swift implementation
public static func restoreFatalError() {
fatalErrorClosure = defaultFatalErrorClosure
}
}
In your test target add the following:
import Foundation
import XCTest
extension XCTestCase {
func expectFatalError(expectedMessage: String, testcase: @escaping () -> Void) {
// arrange
let expectation = self.expectation(description: "expectingFatalError")
var assertionMessage: String? = nil
// override fatalError. This will pause forever when fatalError is called.
FatalErrorUtil.replaceFatalError { message, _, _ in
assertionMessage = message
expectation.fulfill()
unreachable()
}
// act, perform on separate thead because a call to fatalError pauses forever
DispatchQueue.global(qos: .userInitiated).async(execute: testcase)
waitForExpectations(timeout: 0.1) { _ in
// assert
XCTAssertEqual(assertionMessage, expectedMessage)
// clean up
FatalErrorUtil.restoreFatalError()
}
}
}
Test case:
class TestCase: XCTestCase {
func testExpectPreconditionFailure() {
expectFatalError(expectedMessage: "boom!") {
doSomethingThatCallsFatalError()
}
}
}