Edition III

The Masilotti Monthly

Testing, development, and product sneak peeks. Sign up for my monthly newsletter.


20 people have subscribed since last month’s edition. If this is your first Masilotti Monthly, welcome to the family!

This month was another deep dive into Swift testing with XCTest. I experimented with a micro-library and put together a huge list of XCTest tips and tricks.

Outside of Swift testing, I’ve been leveling up my general programming skills by working through a few online courses and books that have proven really valuable.

Swift testing with XCTest

Last week I threw together a Twitter thread on a few XCTest tips and tricks I’ve picked up over the years. Turns out, this format works really well! The thread was seen over 25,000 times, easily my most popular tweet in a long time.

XCTest tips and tricks tweet analytics

I expanded this into a full blog post with more extensive (and formatted) code examples. There’s 11 tips ranging from little known secrets like XCTUnwrap to general best practices like setting continueAfterFailure to false.

XCTUnwrap

If you haven’t used it before, XCTUnwrap is a great way to unwrap optionals under test. Force unwrapping nil crashes your test suite while XCTUnwrap simply fails your test.

This translates to catching test failures sooner and more reliable continuous integration reporting. Also, the syntax is easy to understand and the naming is obvious. Both are important aspects to look for when introducing new helpers to your test suite.

let data: [String: Any] = [ "name": "Joe" ]
let name = try XCTUnwrap(data["name"] as? String)
XCTAssertEqual(name, "Joe")

Experimenting with better test names

If you’re sick of naming your tests with underscores and camel case then this technique will be of interest to you.

I developed a micro-library (5 lines of code!) that lets you use strings as test names in XCTest. This has huge benefits for readability as we are much better at parsing spaces than camel case.

class TestCase: XCTestCase {
    // before
    func test_tappingTheButton_showsTheLabel() { /* ... */ }

    // after
    func test() {
        test("tapping the button shows the label") { /* ... */ }
    }
}

I’ve been using this approach for some of my tests and am really loving how they read. But before you dive in make sure you understand the tradeoffs.

Read more and get the code in Improving XCTest test name readability.

Sneak peek at an upcoming article on XCTKVOExpectation

I’m working on an in-depth article on how to effectively use XCTKVOExpectation in your test suite. This helper is great for observing asynchronous behavior for existing Apple frameworks, namely UIKit.

Where it really shines is using this for your own mocks. You can add a few annotations to your class and use the helper to wait for different properties to change their value.

This is extremely powerful because if the observed property changes on another thread you don’t need to tick the run loop. Keep an eye out for this article, which should be published in the next few weeks!

ViewInspector

How do you test your SwiftUI views? Apple recommends a combination of Live Previews and snapshot testing. But I’ve found that neither of them gel really well with my workflow, especially when I’m TDDing.

ViewInspector, a third-party library from Alexey Naumov, aims to bridge that gap. It uses the Swift reflection API Mirror to create a representable struct of your rendered view.

The good news is that when it works it works really well. I’m particularly impressed with the “find” API because it decouples your tests from your view hierarchy.

try sut.inspect().findAll(ViewType.Text.self, where: { label in
    try label.text == "A label"
})

However, there are a few downsides. Working with navigation views choke up the finder. Because of some SwiftUI internals I’m not going to pretend to understand, there’s a lot of magic that happens when you use these views. And ViewInspector hasn’t (yet!) figured out a way to handle them gracefully.

Have you played around with ViewInspector? What’s your impression?

Becoming a better programmer

I’ve been dedicating a half hour every day to improving my programming skills. This month was split between two channels: Object-Oriented Programming and the coordinator pattern.

Object-Oriented Programming

Practical Object-Oriented Design: An Agile Primer Using Ruby proved itself in the first two chapters. Don’t be put off because it’s written for Ruby, most of the techniques apply to all OOP languages (like Swift!).

I found this book valuable because of how simple and concise the examples are expressed. Each code sample has practically zero cruft; every line means something and proves a small point.

Here are three tips I picked up that you can apply today (in Swift):

  1. Checking for type is a code smell - instead, ensure those objects conform to a protocol and pass the same message to each
  2. Avoid describing a class with “and” or “or” - objects should only do one thing
  3. Writing code first is expensive - write down the flow of messages before coding

The coordinator pattern on Cocoacasts

More Swift focused, I’ve been working my way through Bart Jacobs’s Cocoacasts course Mastering Navigation With Coordinators. His videos are really well done, concise, and right to the point. I usually watch one each day over lunch.

This course has special interest to me. I somehow missed the whole coordinator movement when Soroush Khanlou spoke about them way back in 2015!

The biggest benefit of the coordinator pattern is removing navigation from your view controllers. This means that no controller knows about any other controller.

Freeing up this dependency enables an app architecture that is much easier to change and scale. All the “routing and plumbing” lives in your coordinators, not the view controllers.

Thanks for reading!

Before you go, I have one question and one request.

First, what do you find is the hardest part of testing Swift code? You can email me or mention/DM me on Twitter.

Second, if you enjoyed this newsletter I would really appreciate it if you could share it with another iOS developer. Thanks in advance!

Testing, development, and product sneak peeks. Sign up for my monthly newsletter.