Forked from mohamede1945/ProgrammerAssertions.swift
Created
September 18, 2020 10:40
-
-
Save alemar11/febf0f63590e07c798e53c3a0f2c6c2b to your computer and use it in GitHub Desktop.
This code is deprecated. You can use https://github.com/mattgallagher/CwlPreconditionTesting instead.
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
// | |
// ProgrammerAssertions.swift | |
// Assertions | |
// | |
// Created by Mohamed Afifi on 12/20/15. | |
// Copyright © 2015 mohamede1945. All rights reserved. | |
// | |
import Foundation | |
/// ### IMPORTANT HOW TO USE ### | |
/// 1. Drop `ProgrammerAssertions.swift` to the target of your app or framework under test. Just besides your source code. | |
/// 2. Drop `XCTestCase+ProgrammerAssertions.swift` to your test target. Just besides your test cases. | |
/// 3. Use `assert`, `assertionFailure`, `precondition`, `preconditionFailure` and `fatalError` normally as you always do. | |
/// 4. Unit test them with the new methods `expectAssert`, `expectAssertionFailure`, `expectPrecondition`, `expectPreconditionFailure` and `expectFatalError`. | |
/// | |
/// This file is an overriden implementation of Swift assertions functions. | |
/// For a complete project example see <https://github.com/mohamede1945/AssertionsTestingExample> | |
/// drop-in replacements | |
public func assert(@autoclosure condition: () -> Bool, @autoclosure _ message: () -> String = "", file: StaticString = __FILE__, line: UInt = __LINE__) { | |
Assertions.assertClosure(condition(), message(), file, line) | |
} | |
public func assertionFailure(@autoclosure message: () -> String = "", file: StaticString = __FILE__, line: UInt = __LINE__) { | |
Assertions.assertionFailureClosure(message(), file, line) | |
} | |
public func precondition(@autoclosure condition: () -> Bool, @autoclosure _ message: () -> String = "", file: StaticString = __FILE__, line: UInt = __LINE__) { | |
Assertions.preconditionClosure(condition(), message(), file, line) | |
} | |
@noreturn public func preconditionFailure(@autoclosure message: () -> String = "", file: StaticString = __FILE__, line: UInt = __LINE__) { | |
Assertions.preconditionFailureClosure(message(), file, line) | |
runForever() | |
} | |
@noreturn public func fatalError(@autoclosure message: () -> String = "", file: StaticString = __FILE__, line: UInt = __LINE__) { | |
Assertions.fatalErrorClosure(message(), file, line) | |
runForever() | |
} | |
/// Stores custom assertions closures, by default it points to Swift functions. But test target can override them. | |
public class Assertions { | |
public static var assertClosure = swiftAssertClosure | |
public static var assertionFailureClosure = swiftAssertionFailureClosure | |
public static var preconditionClosure = swiftPreconditionClosure | |
public static var preconditionFailureClosure = swiftPreconditionFailureClosure | |
public static var fatalErrorClosure = swiftFatalErrorClosure | |
public static let swiftAssertClosure = { Swift.assert($0, $1, file: $2, line: $3) } | |
public static let swiftAssertionFailureClosure = { Swift.assertionFailure($0, file: $1, line: $2) } | |
public static let swiftPreconditionClosure = { Swift.precondition($0, $1, file: $2, line: $3) } | |
public static let swiftPreconditionFailureClosure = { Swift.preconditionFailure($0, file: $1, line: $2) } | |
public static let swiftFatalErrorClosure = { Swift.fatalError($0, file: $1, line: $2) } | |
} | |
/// This is a `noreturn` function that runs forever and doesn't return. | |
/// Used by assertions with `@noreturn`. | |
@noreturn private func runForever() { | |
repeat { | |
NSRunLoop.currentRunLoop().run() | |
} while (true) | |
} |
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
// | |
// ProgrammerAssertionsExampleDivide.swift | |
// Assertions | |
// | |
// Created by Mohamed Afifi on 12/20/15. | |
// Copyright © 2015 mohamede1945. All rights reserved. | |
// | |
import UIKit | |
/// ### IMPORTANT HOW TO USE ### | |
/// 1. Drop `ProgrammerAssertions.swift` to the target of your app or framework under test. Just besides your source code. | |
/// 2. Drop `XCTestCase+ProgrammerAssertions.swift` to your test target. Just besides your test cases. | |
/// 3. Use `assert`, `assertionFailure`, `precondition`, `preconditionFailure` and `fatalError` normally as you always do. | |
/// 4. Unit test them with the new methods `expectAssert`, `expectAssertionFailure`, `expectPrecondition`, `expectPreconditionFailure` and `expectFatalError`. | |
/// | |
/// This file is just an example of how to use the functions. Normally as you would do previously. | |
/// For a complete project example see <https://github.com/mohamede1945/AssertionsTestingExample> | |
func divideAssert(x: Float, by y: Float) -> Float { | |
assert(y != 0, "Zero division") | |
return x / y | |
} | |
func divideAssertionFailure(x: Float, by y: Float) -> Float { | |
guard y != 0 else { | |
assertionFailure("Zero division") | |
return Float.infinity | |
} | |
return x / y | |
} | |
func dividePrecondition(x: Float, by y: Float) -> Float { | |
precondition(y != 0, "Zero division") | |
return x / y | |
} | |
func dividePreconditionFailure(x: Float, by y: Float) -> Float { | |
guard y != 0 else { | |
preconditionFailure("Zero division") | |
} | |
return x / y | |
} | |
func divideFatalError(x: Float, by y: Float) -> Float { | |
guard y != 0 else { | |
fatalError("Zero division") | |
} | |
return x / y | |
} |
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
// | |
// ProgrammerAssertionsExampleTests.swift | |
// AssertionsTests | |
// | |
// Created by Mohamed Afifi on 12/20/15. | |
// Copyright © 2015 mohamede1945. All rights reserved. | |
// | |
import XCTest | |
@testable import Assertions | |
/// ### IMPORTANT HOW TO USE ### | |
/// 1. Drop `ProgrammerAssertions.swift` to the target of your app or framework under test. Just besides your source code. | |
/// 2. Drop `XCTestCase+ProgrammerAssertions.swift` to your test target. Just besides your test cases. | |
/// 3. Use `assert`, `assertionFailure`, `precondition`, `preconditionFailure` and `fatalError` normally as you always do. | |
/// 4. Unit test them with the new methods `expectAssert`, `expectAssertionFailure`, `expectPrecondition`, `expectPreconditionFailure` and `expectFatalError`. | |
/// | |
/// This file is just an example of how to unit test the functions. | |
/// When you run tests, you should expect many of them to fail. This is normally, because the test cases tests the error message produced by expectXXX methods. | |
/// For a complete project example see <https://github.com/mohamede1945/AssertionsTestingExample> | |
class AssertionsTestsExample: XCTestCase { | |
// assert tests | |
func testAssertNotCalled() { | |
expectAssert("Zero division") { | |
} | |
} | |
func testAssertTrueCondition() { | |
expectAssert("Zero division") { | |
divideAssert(1, by: 1) | |
} | |
} | |
func testAssertNoMessage() { | |
expectAssert() { | |
divideAssert(1, by: 0) | |
} | |
} | |
func testAssertErrorIncorrectMessage() { | |
expectAssert("Invalid") { | |
divideAssert(1, by: 0) | |
} | |
} | |
func testAssertCorrectMessage() { | |
expectAssert("Zero division") { | |
divideAssert(1, by: 0) | |
} | |
} | |
// expectionFailure tests | |
func testAssertionFailureNotCalled() { | |
expectAssertionFailure("Zero division") { | |
divideAssertionFailure(1, by: 1) | |
} | |
} | |
func testAssertionFailureNoMessage() { | |
expectAssertionFailure() { | |
divideAssertionFailure(1, by: 0) | |
} | |
} | |
func testAssertionFailureErrorIncorrectMessage() { | |
expectAssertionFailure("Invalid") { | |
divideAssertionFailure(1, by: 0) | |
} | |
} | |
func testAssertionFailureCorrectMessage() { | |
expectAssertionFailure("Zero division") { | |
divideAssertionFailure(1, by: 0) | |
} | |
} | |
// precondition tests | |
func testPreconditionNotCalled() { | |
expectPrecondition("Zero division") { | |
} | |
} | |
func testPreconditionTrueCondition() { | |
expectPrecondition("Zero division") { | |
dividePrecondition(1, by: 1) | |
} | |
} | |
func testPreconditionNoMessage() { | |
expectPrecondition() { | |
dividePrecondition(1, by: 0) | |
} | |
} | |
func testPreconditionErrorIncorrectMessage() { | |
expectPrecondition("Invalid") { | |
dividePrecondition(1, by: 0) | |
} | |
} | |
func testPreconditionCorrectMessage() { | |
expectPrecondition("Zero division") { | |
dividePrecondition(1, by: 0) | |
} | |
} | |
// preconditionFailure tests | |
func testPreconditionFailureNotCalled() { | |
expectPreconditionFailure("Zero division") { | |
dividePreconditionFailure(1, by: 1) | |
} | |
} | |
func testPreconditionFailureNoMessage() { | |
expectPreconditionFailure() { | |
dividePreconditionFailure(1, by: 0) | |
} | |
} | |
func testPreconditionFailureErrorIncorrectMessage() { | |
expectPreconditionFailure("Invalid") { | |
dividePreconditionFailure(1, by: 0) | |
} | |
} | |
func testPreconditionFailureCorrectMessage() { | |
expectPreconditionFailure("Zero division") { | |
dividePreconditionFailure(1, by: 0) | |
} | |
} | |
// fatalError tests | |
func testFatalErrorNotCalled() { | |
expectFatalError("Zero division!") { | |
divideFatalError(1, by: 1) | |
} | |
} | |
func testFatalErrorNoMessage() { | |
expectFatalError() { | |
divideFatalError(1, by: 0) | |
} | |
} | |
func testFatalErrorIncorrectMessage() { | |
expectFatalError("Invalid") { | |
divideFatalError(1, by: 0) | |
} | |
} | |
func testFatalCorrectMessage() { | |
expectFatalError("Zero division") { | |
divideFatalError(1, by: 0) | |
} | |
} | |
} |
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
// | |
// XCTestCase+ProgrammerAssertions.swift | |
// Assertions | |
// | |
// Created by Mohamed Afifi on 12/20/15. | |
// Copyright © 2015 mohamede1945. All rights reserved. | |
// | |
/// ### IMPORTANT HOW TO USE ### | |
/// 1. Drop `ProgrammerAssertions.swift` to the target of your app or framework under test. Just besides your source code. | |
/// 2. Drop `XCTestCase+ProgrammerAssertions.swift` to your test target. Just besides your test cases. | |
/// 3. Use `assert`, `assertionFailure`, `precondition`, `preconditionFailure` and `fatalError` normally as you always do. | |
/// 4. Unit test them with the new methods `expectAssert`, `expectAssertionFailure`, `expectPrecondition`, `expectPreconditionFailure` and `expectFatalError`. | |
/// | |
/// This file is the unit test assertions. | |
/// For a complete project example see <https://github.com/mohamede1945/AssertionsTestingExample> | |
import Foundation | |
import XCTest | |
@testable import Assertions | |
private let noReturnFailureWaitTime = 0.1 | |
public extension XCTestCase { | |
/** | |
Expects an `assert` to be called with a false condition. | |
If `assert` not called or the assert's condition is true, the test case will fail. | |
- parameter expectedMessage: The expected message to be asserted to the one passed to the `assert`. If nil, then ignored. | |
- parameter file: The file name that called the method. | |
- parameter line: The line number that called the method. | |
- parameter testCase: The test case to be executed that expected to fire the assertion method. | |
*/ | |
public func expectAssert( | |
expectedMessage: String? = nil, | |
file: StaticString = __FILE__, | |
line: UInt = __LINE__, | |
testCase: () -> Void | |
) { | |
expectAssertionReturnFunction("assert", file: file, line: line, function: { (caller) -> () in | |
Assertions.assertClosure = { condition, message, _, _ in | |
caller(condition, message) | |
} | |
}, expectedMessage: expectedMessage, testCase: testCase) { () -> () in | |
Assertions.assertClosure = Assertions.swiftAssertClosure | |
} | |
} | |
/** | |
Expects an `assertionFailure` to be called. | |
If `assertionFailure` not called, the test case will fail. | |
- parameter expectedMessage: The expected message to be asserted to the one passed to the `assertionFailure`. If nil, then ignored. | |
- parameter file: The file name that called the method. | |
- parameter line: The line number that called the method. | |
- parameter testCase: The test case to be executed that expected to fire the assertion method. | |
*/ | |
public func expectAssertionFailure( | |
expectedMessage: String? = nil, | |
file: StaticString = __FILE__, | |
line: UInt = __LINE__, | |
testCase: () -> Void | |
) { | |
expectAssertionReturnFunction("assertionFailure", file: file, line: line, function: { (caller) -> () in | |
Assertions.assertionFailureClosure = { message, _, _ in | |
caller(false, message) | |
} | |
}, expectedMessage: expectedMessage, testCase: testCase) { () -> () in | |
Assertions.assertionFailureClosure = Assertions.swiftAssertionFailureClosure | |
} | |
} | |
/** | |
Expects an `precondition` to be called with a false condition. | |
If `precondition` not called or the precondition's condition is true, the test case will fail. | |
- parameter expectedMessage: The expected message to be asserted to the one passed to the `precondition`. If nil, then ignored. | |
- parameter file: The file name that called the method. | |
- parameter line: The line number that called the method. | |
- parameter testCase: The test case to be executed that expected to fire the assertion method. | |
*/ | |
public func expectPrecondition( | |
expectedMessage: String? = nil, | |
file: StaticString = __FILE__, | |
line: UInt = __LINE__, | |
testCase: () -> Void | |
) { | |
expectAssertionReturnFunction("precondition", file: file, line: line, function: { (caller) -> () in | |
Assertions.preconditionClosure = { condition, message, _, _ in | |
caller(condition, message) | |
} | |
}, expectedMessage: expectedMessage, testCase: testCase) { () -> () in | |
Assertions.preconditionClosure = Assertions.swiftPreconditionClosure | |
} | |
} | |
/** | |
Expects an `preconditionFailure` to be called. | |
If `preconditionFailure` not called, the test case will fail. | |
- parameter expectedMessage: The expected message to be asserted to the one passed to the `preconditionFailure`. If nil, then ignored. | |
- parameter file: The file name that called the method. | |
- parameter line: The line number that called the method. | |
- parameter testCase: The test case to be executed that expected to fire the assertion method. | |
*/ | |
public func expectPreconditionFailure( | |
expectedMessage: String? = nil, | |
file: StaticString = __FILE__, | |
line: UInt = __LINE__, | |
testCase: () -> Void | |
) { | |
expectAssertionNoReturnFunction("preconditionFailure", file: file, line: line, function: { (caller) -> () in | |
Assertions.preconditionFailureClosure = { message, _, _ in | |
caller(message) | |
} | |
}, expectedMessage: expectedMessage, testCase: testCase) { () -> () in | |
Assertions.preconditionFailureClosure = Assertions.swiftPreconditionFailureClosure | |
} | |
} | |
/** | |
Expects an `fatalError` to be called. | |
If `fatalError` not called, the test case will fail. | |
- parameter expectedMessage: The expected message to be asserted to the one passed to the `fatalError`. If nil, then ignored. | |
- parameter file: The file name that called the method. | |
- parameter line: The line number that called the method. | |
- parameter testCase: The test case to be executed that expected to fire the assertion method. | |
*/ | |
public func expectFatalError( | |
expectedMessage: String? = nil, | |
file: StaticString = __FILE__, | |
line: UInt = __LINE__, | |
testCase: () -> Void) { | |
expectAssertionNoReturnFunction("fatalError", file: file, line: line, function: { (caller) -> () in | |
Assertions.fatalErrorClosure = { message, _, _ in | |
caller(message) | |
} | |
}, expectedMessage: expectedMessage, testCase: testCase) { () -> () in | |
Assertions.fatalErrorClosure = Assertions.swiftFatalErrorClosure | |
} | |
} | |
// MARK:- Private Methods | |
private func expectAssertionReturnFunction( | |
functionName: String, | |
file: StaticString, | |
line: UInt, | |
function: (caller: (Bool, String) -> Void) -> Void, | |
expectedMessage: String? = nil, | |
testCase: () -> Void, | |
cleanUp: () -> () | |
) { | |
let expectation = expectationWithDescription(functionName + "-Expectation") | |
var assertion: (condition: Bool, message: String)? = nil | |
function { (condition, message) -> Void in | |
assertion = (condition, message) | |
expectation.fulfill() | |
} | |
// perform on the same thread since it will return | |
testCase() | |
waitForExpectationsWithTimeout(0) { _ in | |
defer { | |
// clean up | |
cleanUp() | |
} | |
guard let assertion = assertion else { | |
XCTFail(functionName + " is expected to be called.", file: file.stringValue, line: line) | |
return | |
} | |
XCTAssertFalse(assertion.condition, functionName + " condition expected to be false", file: file.stringValue, line: line) | |
if let expectedMessage = expectedMessage { | |
// assert only if not nil | |
XCTAssertEqual(assertion.message, expectedMessage, functionName + " called with incorrect message.", file: file.stringValue, line: line) | |
} | |
} | |
} | |
private func expectAssertionNoReturnFunction( | |
functionName: String, | |
file: StaticString, | |
line: UInt, | |
function: (caller: (String) -> Void) -> Void, | |
expectedMessage: String? = nil, | |
testCase: () -> Void, | |
cleanUp: () -> () | |
) { | |
let expectation = expectationWithDescription(functionName + "-Expectation") | |
var assertionMessage: String? = nil | |
function { (message) -> Void in | |
assertionMessage = message | |
expectation.fulfill() | |
} | |
// act, perform on separate thead because a call to function runs forever | |
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), testCase) | |
waitForExpectationsWithTimeout(noReturnFailureWaitTime) { _ in | |
defer { | |
// clean up | |
cleanUp() | |
} | |
guard let assertionMessage = assertionMessage else { | |
XCTFail(functionName + " is expected to be called.", file: file.stringValue, line: line) | |
return | |
} | |
if let expectedMessage = expectedMessage { | |
// assert only if not nil | |
XCTAssertEqual(assertionMessage, expectedMessage, functionName + " called with incorrect message.", file: file.stringValue, line: line) | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment