Skip to content

Instantly share code, notes, and snippets.

@seonar22
Forked from KingOfBrian/XCTestAndNil.md
Created December 2, 2023 13:48

Revisions

  1. @KingOfBrian KingOfBrian revised this gist Feb 14, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion XCTestAndNil.md
    Original file line number Diff line number Diff line change
    @@ -16,7 +16,7 @@ class TestCaseDefault: XCTestCase {
    }
    ```

    The asserts following `XCTAssertNotNil` are where things can get ugly. It's very common for `!` usage to sneak in, and force-unwrapping a `nil` value will cause your tests to crash and bail early, meaning that the rest of your tests will not run.
    The asserts following `XCTAssertNotNil` are where things can get ugly. It's very common for `!` usage to sneak in. Force-unwrapping a `nil` value will crash your unit tests, and the remaining unit tests will not be ran or reported to CI. The unit tests still fail, so this isn't a show stopper, but it's not ideal.

    ## The Solution

  2. @KingOfBrian KingOfBrian revised this gist Feb 13, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion XCTestAndNil.md
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    # XCTest and nil
    # XCTest and Optional Unwrapping

    [XCTest](https://developer.apple.com/reference/xctest) is the default test harness on iOS an Apple’s other platforms. It provides support for organizing test cases and asserting expectations in your application code, and reporting the status of those expectations. It's not as fancy as some of the [BDD](https://en.wikipedia.org/wiki/Behavior-driven_development) frameworks like [Quick](https://github.com/Quick/Quick) and [Cedar](https://github.com/pivotal/cedar), but it has gotten much better than it used to be, and is my preferred test framework these days.

  3. @KingOfBrian KingOfBrian revised this gist Feb 13, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion XCTestAndNil.md
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@

    ## The Problem

    One place where the XCTest assertion utilities fall a bit short has been with managing Optional variables in swift. The default use of `XCTAssert` don't provide any mechanism for unwrapping, easily leading to assertion checks like this:
    One place where the XCTest assertion utilities fall a bit short has been with managing Optional variables in swift. The default use of `XCTAssertNotNil` don't provide any mechanism for unwrapping, easily leading to assertion checks like this:

    ```swift
    class TestCaseDefault: XCTestCase {
  4. @KingOfBrian KingOfBrian revised this gist Feb 13, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion XCTestAndNil.md
    Original file line number Diff line number Diff line change
    @@ -39,8 +39,8 @@ This helps clean up our `nil` check by using more typical swift Optional express
    ```swift
    struct UnexpectedNilVariableError: Error {}
    func UnwrapAndAssertNotNil<T>(_ variable: T?, message: String = "Unexpected nil variable", file: StaticString = #file, line: UInt = #line) throws -> T {
    XCTAssertNotNil(variable, message, file: file, line: line)
    guard let variable = variable else {
    XCTFail(message, file: file, line: line)
    throw UnexpectedNilVariableError()
    }
    return variable
  5. @KingOfBrian KingOfBrian revised this gist Feb 13, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion XCTestAndNil.md
    Original file line number Diff line number Diff line change
    @@ -34,7 +34,7 @@ class TestCaseThrows: XCTestCase {
    }
    ```

    This helps clean up our `nil` check by using more typical swift Optional expressions. This will stop the test if it encounters `nil`, and allows the remainder of the test to use an unwrapped value. This is a good start, except `XCTestCase` doesn't report the error location correctly. If the test function throws, it does not point to the line that threw the exception. But not to worry: this is easy to clean up with a little wrapper that uses the `#file` and `#line` default values.
    This helps clean up our `nil` check by using more typical swift Optional expressions. This will stop the test if it encounters `nil`, and allows the remainder of the test to use an unwrapped value. This is a good start, except `XCTestCase` doesn't report the error location correctly. If the test function throws, XCode points to the test function as the source of the failure, not the line that threw the exception. But not to worry: this is easy to clean up with a little wrapper that uses the `#file` and `#line` default values.

    ```swift
    struct UnexpectedNilVariableError: Error {}
  6. @KingOfBrian KingOfBrian revised this gist Feb 13, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion XCTestAndNil.md
    Original file line number Diff line number Diff line change
    @@ -20,7 +20,7 @@ The asserts following `XCTAssertNotNil` are where things can get ugly. It's very

    ## The Solution

    A nice solution is possible, due to an often-overlooked feature of `XCTestCase`. If the test function is marked with `throws`, any thrown exceptions will cause the test to fail. We can use this to fail our tests using normal Swift flow control mechanisms:
    A nice solution is possible, due to an often-overlooked feature of `XCTestCase`. If the test function is marked with `throws`, any thrown exception will cause the test to fail. We can use this to fail our tests using normal Swift flow control mechanisms:

    ```swift
    class TestCaseThrows: XCTestCase {
  7. @KingOfBrian KingOfBrian revised this gist Feb 13, 2017. 1 changed file with 7 additions and 12 deletions.
    19 changes: 7 additions & 12 deletions XCTestAndNil.md
    Original file line number Diff line number Diff line change
    @@ -1,13 +1,12 @@
    # XCTest and nil

    XCTest is the default test harness on iOS. It provides support for organizing test cases and asserting expectations in your application code, and reporting the status of those expectations. It's not as fancy as some of the BDD frameworks like `Quick` and `Cedar`, but it has gotten much better, and is my preferred test framework these days.
    [XCTest](https://developer.apple.com/reference/xctest) is the default test harness on iOS an Apple’s other platforms. It provides support for organizing test cases and asserting expectations in your application code, and reporting the status of those expectations. It's not as fancy as some of the [BDD](https://en.wikipedia.org/wiki/Behavior-driven_development) frameworks like [Quick](https://github.com/Quick/Quick) and [Cedar](https://github.com/pivotal/cedar), but it has gotten much better than it used to be, and is my preferred test framework these days.

    ## The Problem

    One place where the XCTest assertion utilites fall a bit short has been with managing Optional variables in swift. The default use of `XCTAssert` don't provide any mechanism for un-wrapping, easily leading to assertion checks like this:
    One place where the XCTest assertion utilities fall a bit short has been with managing Optional variables in swift. The default use of `XCTAssert` don't provide any mechanism for unwrapping, easily leading to assertion checks like this:

    ```swift

    class TestCaseDefault: XCTestCase {
    func testAnOptional() {
    let string: String? = nil
    @@ -17,26 +16,25 @@ class TestCaseDefault: XCTestCase {
    }
    ```

    The asserts following `XCTAssertNotNil` are where things can get real ugly. It's very common for `!` usage to sneak in, and cause your tests to crash after a failure, which causes the rest of your tests to not run.

    The asserts following `XCTAssertNotNil` are where things can get ugly. It's very common for `!` usage to sneak in, and force-unwrapping a `nil` value will cause your tests to crash and bail early, meaning that the rest of your tests will not run.

    ## The Solution

    A nice solution is possible, due to an often overlooked feature of XCTestCase. If the test function is marked with `throws` any thrown exceptions will cause the test to fail. We can use this to fail our tests using normal swift flow control mechanisms.
    A nice solution is possible, due to an often-overlooked feature of `XCTestCase`. If the test function is marked with `throws`, any thrown exceptions will cause the test to fail. We can use this to fail our tests using normal Swift flow control mechanisms:

    ```swift
    class TestCaseThrows: XCTestCase {
    struct UnexpectedNilVariableError: Error {}
    func testAnOptional() throws {
    let string: String? = nil
    guard let newString = string else { throw UnexpectedNilVariableError() }
    // newString is Unwrapped, and things are happy
    // newString is unwrapped, and things are happy
    XCTAssert(newString.lengthOfBytes(using: .utf8) > 0)
    }
    }
    ```

    This helps clean up our nil check by using more typical swift Optional expressions. This will stop the test if `nil` is encountered and allow the remainder of our test to use an unwrapped value. This is a good start, except `XCTestCase` doesn't report the error location correctly. If the test function throws, it does not point to the line that threw the exception. But not to worry, this is easy to clean up with a little wrapper that uses the `#file` and `#line` default values.
    This helps clean up our `nil` check by using more typical swift Optional expressions. This will stop the test if it encounters `nil`, and allows the remainder of the test to use an unwrapped value. This is a good start, except `XCTestCase` doesn't report the error location correctly. If the test function throws, it does not point to the line that threw the exception. But not to worry: this is easy to clean up with a little wrapper that uses the `#file` and `#line` default values.

    ```swift
    struct UnexpectedNilVariableError: Error {}
    @@ -57,7 +55,4 @@ class TestCaseUnwrap: XCTestCase {
    }
    ```

    Now we have a nice swifty test helper to manage nil checks. I hope this helps clean up your test code. And remember, `!` is rarely a good answer, even in tests!



    Now we have a nice Swifty test helper to manage `nil` checks. I hope this helps clean up your test code. And remember, `!` is rarely a good answer, even in tests!
  8. @KingOfBrian KingOfBrian revised this gist Feb 13, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion XCTestAndNil.md
    Original file line number Diff line number Diff line change
    @@ -36,7 +36,7 @@ class TestCaseThrows: XCTestCase {
    }
    ```

    This really helps clean up the usage of our `XCTAssertNotNil` with a bit more typical swift Optional usage, and stops the test if nil is encountered. This is pretty nice, except that `XCTestCase` doesn't report the error location correctly, which is less than ideal. Not to worry, this is easy to clean up with the `#file` and `#line` default values.
    This helps clean up our nil check by using more typical swift Optional expressions. This will stop the test if `nil` is encountered and allow the remainder of our test to use an unwrapped value. This is a good start, except `XCTestCase` doesn't report the error location correctly. If the test function throws, it does not point to the line that threw the exception. But not to worry, this is easy to clean up with a little wrapper that uses the `#file` and `#line` default values.

    ```swift
    struct UnexpectedNilVariableError: Error {}
  9. @KingOfBrian KingOfBrian created this gist Feb 13, 2017.
    63 changes: 63 additions & 0 deletions XCTestAndNil.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,63 @@
    # XCTest and nil

    XCTest is the default test harness on iOS. It provides support for organizing test cases and asserting expectations in your application code, and reporting the status of those expectations. It's not as fancy as some of the BDD frameworks like `Quick` and `Cedar`, but it has gotten much better, and is my preferred test framework these days.

    ## The Problem

    One place where the XCTest assertion utilites fall a bit short has been with managing Optional variables in swift. The default use of `XCTAssert` don't provide any mechanism for un-wrapping, easily leading to assertion checks like this:

    ```swift

    class TestCaseDefault: XCTestCase {
    func testAnOptional() {
    let string: String? = nil
    XCTAssertNotNil(string)
    XCTAssert((string?.lengthOfBytes(using: .utf8))! > 0)
    }
    }
    ```

    The asserts following `XCTAssertNotNil` are where things can get real ugly. It's very common for `!` usage to sneak in, and cause your tests to crash after a failure, which causes the rest of your tests to not run.


    ## The Solution

    A nice solution is possible, due to an often overlooked feature of XCTestCase. If the test function is marked with `throws` any thrown exceptions will cause the test to fail. We can use this to fail our tests using normal swift flow control mechanisms.

    ```swift
    class TestCaseThrows: XCTestCase {
    struct UnexpectedNilVariableError: Error {}
    func testAnOptional() throws {
    let string: String? = nil
    guard let newString = string else { throw UnexpectedNilVariableError() }
    // newString is Unwrapped, and things are happy
    XCTAssert(newString.lengthOfBytes(using: .utf8) > 0)
    }
    }
    ```

    This really helps clean up the usage of our `XCTAssertNotNil` with a bit more typical swift Optional usage, and stops the test if nil is encountered. This is pretty nice, except that `XCTestCase` doesn't report the error location correctly, which is less than ideal. Not to worry, this is easy to clean up with the `#file` and `#line` default values.

    ```swift
    struct UnexpectedNilVariableError: Error {}
    func UnwrapAndAssertNotNil<T>(_ variable: T?, message: String = "Unexpected nil variable", file: StaticString = #file, line: UInt = #line) throws -> T {
    XCTAssertNotNil(variable, message, file: file, line: line)
    guard let variable = variable else {
    throw UnexpectedNilVariableError()
    }
    return variable
    }

    class TestCaseUnwrap: XCTestCase {
    func testUnwrap() throws {
    let string: String? = nil
    let newString = try UnwrapAndAssertNotNil(string)
    XCTAssert(newString.lengthOfBytes(using: .utf8) > 0)
    }
    }
    ```

    Now we have a nice swifty test helper to manage nil checks. I hope this helps clean up your test code. And remember, `!` is rarely a good answer, even in tests!