How to Use Protocols for Swift Mocking and Stubbing 

 November 8, 2016

by Jon Reid


You’re writing a unit test in Swift. You feel the need to avoid interactions with a real object. You want to provide a fake object, a mock, or a “test double” (like a stunt double). But Swift is strict about types! How can we do Swift mocking?

[This post is part of the series TDD Sample App: The Complete Collection …So Far]

Why Would You Want Swift Mocking?

When should you use a test double, like a stub/mock/fake? For me, Swift mocking comes down to needing two things:

  1. Speed
  2. Reliability

I’m all about speed because fast feedback is the main benefit of test-driven development. The “TDD Waltz” of Red-Green-Refactor is best when the waltz moves quickly. The faster you can iterate, the more you stay “in the zone” of enhanced productivity.

Reliability is absolutely necessary for unit tests. A test should pass, or it should fail. When a test is flaky, there’s often global state involved (like UserDefaults). Or there’s continually changing state (like Date).

So, speed and reliability drive the need for test doubles. An example of something that’s both slow and unreliable is networking.

It Was So Easy in Objective-C…

Supplying test doubles is incredibly easy to do in Objective-C. Based on its Smalltalk roots, Objective-C’s central mechanism is messages. You can send any message to any object. (The object may or may not be happy about receiving the message.)

So if your code sends a quack message to an object, all that matters is that the object can quack. Objective-C’s types are suggestions that help us avoid a ton of errors. But you can defeat compile-time type-checking by casting an object to id.

There’s a danger that the interface of the real object can diverge from the fake. If your test sends a message that’s honored by the fake but unknown to the real thing, you’ll crash (with passing tests). This danger can be completely avoided, though. Mocking frameworks like OCMock and OCMockito use introspection to create fakes that model the real objects. If a method no longer exists in the real object, the mocking frameworks will complain. No divergence allowed!

In the Objective-C version of my Marvel Browser app, creating a fake version of NSURLSession is this easy:

NSURLSession *mockSession = mock([NSURLSession class]);

It’s not actually an NSURLSession. But because it answers all messages that NSURLSession handles, who’s complaining?

…But I Learned TDD in C++

Swift, on the other hand, will complain. There’s no cheating its strong type-checking. What are we to do?

If I’d started TDD in Objective-C, I might feel a little lost. Thankfully, I traveled a harder road. I learned Test-Driven Development in C++.

Mock objects were first invented in Java. There, Java programmers initially relied on defining interfaces. Rather than programming to concrete instances, they could define interfaces instead. The interface could be supplied by either a real object or a fake one.

In C++, the way forward was Abstract Base Classes. I learned to define classes with pure virtual member functions. The implementations of these member functions could be supplied by either a real object or a fake one.

Protocols and Extensions: The Keys to Swift Mocking

Java interfaces, C++ abstract base classes, Swift protocols. There are subtle differences, but their basic purpose is the same: to define abstractions that can be provided by concrete types.

(Of course, Objective-C has its protocols as well. But because Objective-C uses true duck-typing, protocols weren’t as important for testing.)

The uniformity of Swift’s protocols helps us write testable code. It doesn’t matter if the concrete implementation is a class, a struct, or an enum.

And thanks to extensions, we can supply additions to types, even standard ones. With an extension, we can retroactively apply a protocol to an existing class—even one we don’t own.

Let’s look at how protocols and extensions play together to give us a testing seam.

Example: A Testing Seam for URLSession

In the Swift version of the Marvel Browser TDD sample app, I want to start defining the FetchCharactersMarvelService. This object will fetch comic characters by sending network requests to the Marvel Comics API.

I prefer unit tests over integration tests. (Speed and reliability, remember?) So we want to write unit tests to drive the creation of this Service object. It will make a network request, which we don’t want in unit tests. What do we do?

To give us testing flexibility, the Service won’t determine its own URLSession. Instead, I’ll use Constructor Injection. The initializer will take an argument:

struct FetchCharactersMarvelService {
    init(session: URLSession) {
        // more to come here

We want production code to provide an actual URLSession:

let service = FetchCharactersMarvelService(session: URLSession.shared)

But test code should be able to provide something different—a different implementation of a protocol. Let’s define the protocol, initially empty:

protocol URLSessionProtocol {}

Now we can use an extension to declare that URLSession conforms to this protocol:

extension URLSession: URLSessionProtocol {}

With this, let’s change the type of the initializer argument from the concrete URLSession to the protocol:

struct FetchCharactersMarvelService {
    init(session: URLSessionProtocol) {
        // more to come here

Because URLSession conforms to our new URLSessionProtocol, the following production code will still be valid:

let service = FetchCharactersMarvelService(session: URLSession.shared)

So now, all we need is for URLSessionProtocol to contain something and some test code that implements the protocol. We’ll explore how to do this in the next article.

Conclusion: The Basic Configuration

Let’s summarize by pulling all the code together, so we can see it all. Here’s the production code for an initializer that can take a URLSession:

protocol URLSessionProtocol {}
extension URLSession: URLSessionProtocol {}
struct FetchCharactersMarvelService {
    init(session: URLSessionProtocol) {
        // more to come here

Next time, we’ll begin exploring what it looks like to create a useful mock object in Swift. In particular, I’ll show common approaches to Swift mocking that can be greatly improved.

Do you have any questions so far? Or any reservations? Please share in the comments below!

[This post is part of the series TDD Sample App: The Complete Collection …So Far]

How to Structure Tests that Do Real Networking

Jon Reid

Programming was fun when I was a kid. But working in Silicon Valley, I saw poor code led to fear, with real human costs. Looking for ways to make my life better, I learned about Extreme Programming, including unit testing, test-driven development (TDD), and refactoring. Programming became fun again! I've now been doing TDD in Apple environments for 20 years. I'm committed to software crafting as a discipline, hoping we can all reach greater effectiveness and joy. Now a coach with Industrial Logic!

  • But wait. With such extensions you allow the production code to pass there literally any object. As protocol is empty. Yes, you’ll need to implement an extension to that object’s class which makes this solution proof to mistakes but it becomes so less fool-proof…

    • The protocol is only initially empty, which means nothing can be called. Then, on as as-needed basis, you copy existing method signatures from the class to the protocol. So we’re not adding anything to the class. Instead, we’re exposing a subset of its methods through the protocol.

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}