XCTest tips and tricks that can level up your Swift testing.

🧵👇
1. Use XCTUnwrap instead of force wrapping optionals

Force unwrapping a nil will cause the test suite to completely crash, meaning no output or reporting. XCTUnwrap provides a nice failure message and fails the test.
2. Set continueAfterFailure to false

If you have multiple assertions in a test they might increase in specificity. The first asserts the size of the array and the second assets the first element. You don't want to run the second if the first fails, it's just noise.
3. Use KVO observing asynchronous expectations

Use XCTKVOExpectation to assert KVO-compliant properties on your subject under test.

let expectation = keyValueObservingExpectation(for: subject, keyPath: "property", expectedValue: value)
wait(for: [expectation], timeout: 1)
4. Assert types with XCTAssert and "is"

This technique is helpful when you only care about the type and not a particular instance.

XCTAssert(controller.presentedViewController is CustomViewController)
5. Keep XCTAssertEqual parameter order consistent

The documentation doesn't indicate if the expected or actual value should be first. Pick one and stick with it to make it obvious which is which for any test. (Personally, I put the actual value first.)
6. Extract helpers judiciously

If I need 3 or more lines of code to "do something" I extract it to a helper. This includes test setup, the action, and the assertion(s). Define the function with #filepath and #line to pass along test failures to the caller.
If you made it this far you might also enjoy reading Masilotti.com, where I blog about testing in Swift.
What other XCTest tips have you picked up? Reply here I'll add them to this thread!
7. Add test extensions to models to make your code easier to understand by reducing noise via @lightsprint09

struct User {
let firstName: String
let lastName: String
}

extension User {
static func build(firstName: String = "Joe", lastName: String = "Masilotti") { ... }
}
8. Conform to Equatable to combine assertions via @lightsprint09

let user = controller.user
XCTAssertEqual(user, User(name: "Joe"))

extension User: Equatable {
func ==(_ lhs: User, _ rhs: User) -> Bool {
lhs.name == rhs.name
}
}
9. Always make your tests throw via @forceunwrap

func test_somethingHappens() throws { }

Adding throws to the end of the test captures any raised exceptions and let's you use "try" without ? or ! throughout the test.
10. Use protocols for the APIs you want to test. Mock extensively via @dasdom

Way too much for a tweet, so here’s an entire blog post! masilotti.com/better-swift-u…

• • •

Missing some Tweet in this thread? You can try to force a refresh
 

Keep Current with Joe Masilotti

Joe Masilotti Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

More from @joemasilotti

2 Feb
I spent most of today reworking the core logic of @TheMugshotBot and really enjoyed it! This is super exciting because it opens up a lot of new opportunities for upcoming themes and customizations. And ironed out a bunch of bugs.
A big help was commenting out the entire model and TDDing the bulk of the logic. I missed a few things on the first pass, but was able to backfill a feature or two and then wrap it in tests.

I'm all for this "TDDish" approach I've developed over the past few years!
Deep in the weeds...

The code had a bunch of features slapped on here and there. Taking the time document the entire flow (on paper!) and then reimplementing helped consolidate a lot of logic and missed edge cases.

All the more reason to dedicate time to refactoring!
Read 4 tweets
19 Jan
I spent exactly 8.5 hours this weekend and built a fully functioning SwiftUI app. It uses open data to map the Heritage Trees of Portland, OR.

It's also open source. I'm planning on continuing to build this in public. github.com/joemasilotti/p…
My first pass was a simple list view showing each tree. This downloads data from an open API provided by the city of Portland.

I experimented with Combine and URLSession's dataTaskPublisher to get this working in MVVM fashion.
portlandmaps.com/metadata/index… Image
Next up was a bare-bones detail screen. I mapped some of the important properties in a new screen via NavigationLink. Image
Read 8 tweets

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Too expensive? Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal Become our Patreon

Thank you for your support!

Follow Us on Twitter!