Testing Asynchronous Operations

How would you go about unit testing your networking calls? They are asynchronous, so the results won’t come back right away. The following is an excerpt of the latest chapter I just added to my book Effective Unit Testing on this exact topic.

All the unit tests that you’ve seen so far are for testing synchronous operations. That is, the outputs can be observed and verified immediately after invoking the method on the test subject. The outputs can be function return values, state changes, or methods invoked on a dependency. All of these happen right away in the same thread. When you write the assertions in the Then phase, you’re guaranteed that the outputs have already been set so that you can safely compare the actual v.s. expected. You don’t have to worry about whether the outputs are ready or not. They are ready.

However, a lot of stuff that we do in modern iOS development are asynchronous operations, such as Core Data, networking, or even some expensive drawing code or events. The results will come, but not right away. It takes time for the task to finish, and the outputs be set. In iOS, these asynchronous operations are usually coded in one of two ways:

  • Completion handler block
  • Delegate method

Asynchronous operations present a challenge to unit testing. In the Then phase, the results may or may not have been set to the outputs for you to observe and verify. When you write your assertions, the test may pass this time (if the outputs have been set), but fail at another time (if the outputs haven’t been set).

The most common asynchronous operation is networking. Fetching data from an API over the network has latency, because data takes time to travel through wires around the globe. There can also be numerous points of failures. The server may be down. Packets can get dropped. Multiplexing can produce errors. Connection may be lost. There are many variables that can result in errors and/or delays. As a result, asynchronous testing necessitates some special handling. Fortunately, Xcode has built-in support to help with that.

As quoted in Apple’s URL Session Programming Guide:

Like most networking APIs, the NSURLSession API is highly asynchronous. If you use the default, system-provided delegate, you must provide a completion handler block that returns data to your app when a transfer finishes successfully or with an error. Alternatively, if you provide your own custom delegate objects, the task objects call those delegates’ methods with data as it is received from the server (or, for file downloads, when the transfer is complete).

Let’s look at completion handler blocks and delegate methods, separately, in more details. We’ll use a networking example that fetches public gists from the GitHub API to illustrate how to write unit tests for asynchronous operations.

Continue reading →

How code isolation helps refactoring

Single responsibility. Separation of concerns. Code isolation. Data encapsulation.

These concepts are so universal. They are easy to accept and take for granted. But do you actually know what they mean? Can you explain them?

I’ll attempt to crystalize these buzzwords/theories/principles by taking a different angle.

There are two types of isolations.

  1. Isolating components using protocols to declare inputs and outputs, and expose only external behaviors.
  2. Isolating features using structs to encapsulate data in separate requests, responses, and view models.

Let’s explore each to understand how clean architecture can facilitate unit testing.

Continue reading →

Why does your app always seem to go awry?

The following story is horrifying, yet very familiar to any professional developer.

Chris the Client: How’s the app coming along? Are we ready to release it?

Dave the Developer: Great! All the features are working perfectly. And I’ve designed these classes to be small and reusable. It’ll be easy to maintain going forward.

Chris the Client: That’s fantastic! Let’s release this version 1 to the App Store.

Dave the Developer: Awesome!

Chris the Client: We’ve got some initial feedback from our users. And we want to add this new feature, and change this to that. How long will it take?

Dave the Developer: Hmm… If we’re going to do that, I’ll need to refactor this part of the app. It’ll take maybe a week.

Chris the Client: Why? I thought you said it would be easy to maintain.

Dave the Developer: Yeah, but your proposed changes will dramatically alter the way how things are working now. It’s not that simple.

Chris the Client: Okay, do whatever it takes. We want to get this out the door as soon as possible.

Dave the Developer: I’ll do my best.

Chris the Client: Some of our users are seeing crashes when they do this. Can you fix it?

Dave the Developer: Let me take a look… Ah, I found where the problem is. It’ll take a couple days to look into fixing it.

Chris the Client: Thanks.

Dave the Developer: This bug is really tricky. But it has been fixed. And I’ve released 1.1 to the App Store.

Chris the Client: Our users are still seeing crashes when they do this. I thought you said it was fixed!?

Dave the Developer: Yes, it was fixed. I’m not sure why it’s still happening. I can take a look.

Chris the Client: Yes, please do.

Continue reading →

Unit Testing and TDD Terminology

This is part 2 of this terminology series:

This method is difficult to test.

How do I mock this class? With a stub or mock?

Should I test private methods?

Testing in the iOS world has been gaining more attention for the last several years. However, it is still in the minority. There’re still many who are just getting their feet wet. If you’re also wondering about these questions, I’ll demystify these vocabularies for you, and give you an overview of what unit testing is, its process, and practice.

Continue reading →

Clean Swift Architecture Terminology

This is part 1 of this terminology series:

What should I name this class, struct, and protocol?

What is the difference between worker, service, and manager?

How should I name this function, method, and parameters?

These questions frequently come up in my mentorship program, especially when you just get started. People coming from other architectures may even get confused or overwhelmed with a bunch of new terms. You may be able to guess 80% of what these mean correctly. But having a shared vocabulary is important to communicate ideas to other developers effectively. You don’t want to waste 30 minutes to just get on the same page on the terminology.

So, let’s clarify the terms used in the Clean Swift Architecture.

Continue reading →