Unwrap optional values in XCTest with XCTUnwrap

Before Xcode 11, when I have to test an optional variable in XCTest, I used to do something like this.

Let's say we have a User model with an optional Address .

struct Address {

let city : String

let country : String

}



struct User {

let name : String

let address : Address ?

}

Not lazy way #

func testAddress ( ) throws {

let userModel : User ? = User ( name : "John" , address : nil )



guard let user = userModel else {

XCTFail ( "Expected non-nil user" )

return

}



guard let address = user . address else {

XCTFail ( "Expected non-nil address" )

return

}



XCTAssertEqual ( address . city , "Bangkok" )

XCTAssertEqual ( address . country , "Thailand" )

}

How you write a test is quite subjective, there is no right or wrong, but I think a separate guard statement sending a clearer message when the test failed.

If the test failed, you would get an error like this:

failed - Expected non - nil address

I know exactly the part that might go wrong when I see something like this. So this is my preferred way of testing optional values.

A quite lazy way #

The following example is like the former one, just a bit lazier. I put multiple optional values under the same guard.

func testAddress ( ) throws {

let userModel : User ? = User ( name : "John" , address : nil )



guard

let user = userModel ,

let address = user . address

else {

XCTFail ( "Expected non-nil user and address" )

return

}



XCTAssertEqual ( address . city , "Bangkok" )

XCTAssertEqual ( address . country , "Thailand" )

}

The error won't be as clear as the first one. You can't be sure whether User or Address is nil .

failed - Expected non - nil user and address

Lazy way #

I used this one a lot since it is the shortest, but the failed message is the least useful.

func testManualOptional3c ( ) throws {

let user : User ? = User ( name : "John" , address : nil )



XCTAssertEqual ( user ? . address ? . city , "Bangkok" )

XCTAssertEqual ( user ? . address ? . country , "Thailand" )

}

You will get two failed cases, which are quite vague. User , Address , or the property itself is nil ; you would never know.

failed : ( "nil" ) is not equal to ( "Optional(" Bangkok ")" )

failed : ( "nil" ) is not equal to ( "Optional(" Thailand ")" )

XCTUnwrap to the rescue #

In Xcode 11, there is a new assertion for the unwrapping. XCTUnwrap is an assertion that tries to unwrap the optional and throw an error (and thus fail the test) if the optional is nil . XCTUnwrap is perfect for this situation.

func testAddress ( ) throws {

let userModel : User ? = User ( name : "John" , address : nil )

let user = try XCTUnwrap ( user )

let address = try XCTUnwrap ( u . address )



XCTAssertEqual ( address . city , "Bangkok" )

XCTAssertEqual ( address . country , "Thailand" )

}

The error is as clear as our first example, but a lot shorter.

failed : expected non - nil value of type "Address"

We need to mark our test with the throws keyword since XCTUnwrap is capable of throwing one.

Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Become a patron

Tweet

Share

← Home