Search code examples
swiftunit-testingxctest

Unit Tests assertionFailure


I'm unit testing:

func testParseFakeDeeplinkSuccess() {
    let fakeDeeplink = "fakeDeeplink"
    let url = touchManager?.handleTouch(["deeplink": fakeDeeplink as NSSecureCoding])
    XCTAssertNotNil(url)
}

which will result in "assertionFailure("3DTouch not trackable")"

    func handleTouch(_ userInfo: [String: NSSecureCoding]?) -> URL? {
    if let shortcut = userInfo?["deeplink"] as? String,
       let url = URL(string: shortcut) {

        switch shortcut {
        default:
            assertionFailure("3DTouch not trackable")
        }

        return url
    }

    return nil
}

What can I use in my unit tests to tests this properly?


Solution

  • You're hitting the assertion error because that's the branch your code reaches once it's evaluated.

    if let shortcut = userInfo?["deeplink"] as? String, let url = URL(string: shortcut) {
      // 1. The branch is taken, unwrapped `shortcut`, it's value is "fakeDeeplink"
    
      switch shortcut {
      default:
        // 2. This is the only branch in the switch, so this case is always executed.
        assertionFailure("3DTouch not trackable")
      }
    }
    

    You might only be seeing a crash in your tests (and not in a production build of your app) because assertionFailure will have no effect in the default -O optimization mode used in RELEASE builds. Tests, by default, will be compiled for DEBUG which uses the -Onone optimization mode causing assertions to trigger a crash.

    If you want to handle an error condition without trapping, you should either return nil or use a throwing method instead:

    struct TouchNotTrackableError: Error { }
    
    func handleTouch(_ userInfo: [String: NSSecureCoding]?) throws -> URL? {
        if let shortcut = userInfo?["deeplink"] as? String,
           let url = URL(string: shortcut) {
    
            switch shortcut {
            default:
                throw TouchNotTrackableError()
            }
    
            return url
        }
    
        return nil
    }
    

    Then you can test if the method throws.

    func testParseFakeDeeplinkSuccess() {
        let fakeDeeplink = "fakeDeeplink"
        XCTAssertThrowsError(try touchManager?.handleTouch(["deeplink": fakeDeeplink as NSSecureCoding]))
    }