Posts

At Oak City Labs, we rely heavily on unit testing in our quality software process. Unit testing is the safety net that lets us reliably improve existing code. Our testing suite double checks that modified code still behaves the way we expect. I’ve written before (here and here) about how we use dependency injection (DI) which makes unit testing easier. DI helps us wrap code we need to test in a special environment. By controlling the environment, we can make sure that the code being tested gives the correct output for a given set of conditions. This works very well for pieces of the application that we can encase in a layer that we control. Unfortunately, we can’t wrap everything.

Consider our API layer. This is the part of our application that talks to the server over the internet. It makes network calls, processes replies and handles errors like slow network responses or no network at all. In testing this code, we want it to behave as normally as possible for accurate testing, so we still want it to make API requests and interpret the results. At the same time, these are unit tests, so they should be fast and not depend on external resources. We don’t want to make requests to a real server on the internet. If that test failed, it wouldn’t be obvious if our code was broken, the server was down or the network cable was mistakenly unplugged. It’s important that our unit tests be self contained so when something fails, we know that a specific section of code has failed and we can fix it ASAP.

Back in the old days, before Swift, we wrote in Objective-C. Swift is a strongly typed language where Objective-C is weakly typed. While weak typing in Objective-C often gave a developer enough rope to hang themselves, it was flexible enough to do interesting things like easily mock pieces of software. Using mocks, fakes and stubs, you could (with some effort) replace pieces of the system software with substitute code that behaved differently.  We could use this to test our API code by changing the way the system network routines worked. Instead of actually contacting a server on the internet, a certain call just might always return a canned response. Our API code wouldn’t change, but when it asked the system to contact the server for new data, our mocked code would run instead and just return the contents of a local file. By building a library of expected calls and prepared responses, we could create a controlled environment to test all our API code.

Swift, on the other hand, brought us strong typing, which wiped out whole classes of bugs and insured that objects were always the type we expected. We paid for this safety with the loss of the ability to easily mock, fake or stub things. Someday, Swift might gain new features that make this possible (maybe even easy) but for now, this is difficult to do with any efficiency. So, we need a new approach for testing Swift API code.

Like we said earlier, we don’t want to use external network resources to test our code because too many things are out of our control. But what if the test server were running on the development machine? In fact, what if the test server were running inside our application? Enter two open source projects — Ambassador and Embassy from the fine folks at Envoy.com.  Embassy is a lightweight, dependency free web server written in Swift. Ambassador is a web framework that sits on top of Embassy and makes it easy to write a mock API server.  

In this new approach to testing our API layer, we’ll write some Ambassador code to define our mock endpoints. We’ll declare what URL path they’ll respond to and what response the endpoint will return. Now, inside our unit test, we’ll fire up the mock server and run it on a background thread.  Since you’re controlling both the client and server side of the request, you can make asserts on both ends of the exchange. On the server side, you can validate the data submitted by the client. On the client side, you can ensure that the response from the server was interpreted correctly. Ambassador has some nice conveniences as well for working with JSON data, introducing delayed responses from the server and other common testings needs.

In order to use our freshly built API mock server, all you need to do is change the server name used by the API layer. This is important because we don’t want to make significant changes to our code in order to test it. We want to test as close as we can to production code. By switching our base URL from “https://api.ourserver.com” to “http://localhost:8080”, we can test all our network requests with no external dependencies. Since we’re using dependency injection, this change is very simple to implement in our unit testing routines.

The move from Objective-C to Swift has allowed us to write cleaner and safer code, but the price we pay is the loss of the fast and loose, über permissive Objective-C runtime environment. Fast and loose always caused more problems than it solved, so I’m happy to see it go. A few of our existing solutions have gone with it, but with a bit of ingenuity, we can move forward with better and safer new solutions.

 

At Oak City Labs, we’re always trying to find ways to improve our code’s stability, testability and maintainability. Today we look at the problem of singletons, global state and how to avoid these landmines in the context of Swift.

The Problem

Let’s talk about global variables. Most everyone can agree that global variables are A Very Bad Thing. Globals are undoubtably a code smell and religiously avoided by experienced programmers. Globals are problematic for lots of reasons, but mostly because of Spooky Action At A Distance. If two pieces of code both access a global variable, they can interact via the global variable, often in unexpected and undesirable ways. In this article, Misko Hevery (the Angular guy) dives deep into the many ways this can go pear shaped quickly.

New developers sometimes try to fix global variables by switching to singletons and maintain state within a magic object that is just a static method call away. I’ve heard more than one developer exclaim “Oh no, we never use global variables. We use singletons instead.” The ugly truth though, is that singletons are really just fancier global variables. You still get Spooky Action. You still have implicit and hidden dependencies. Try to write a unit test on a class that relies on a singleton. You can’t look at the exposed interface for this class (think header file) and know about the secret, hidden dependence on that singleton. You must inspect the code and it’s a matter of tedious and confusing mocking of static class methods to try and make this testable. Then write a second test and discover that the tests fail depending on the order in which they run. It’s crazy making!

The most disappointing aspect of singletons is that Apple uses them so often in their own API — UIScreen, NSNotificationCenter, NSUserDefaults, etc. It seems as if they’re encouraging us to use singletons, even when it makes applications fragile and testing difficult.

The Solution — Dependency Injection

“Dependency Injection” (DI) sounds intimidating. And complicated. And scary. People write long, drawn out blog posts about it. But it’s not nearly as intimidating as you might think. At the core, DI just means that you explicitly supply a class with any dependencies, either through initialization or a method call.

Consider this class that uses a two singletons:

class SocialInfo {
    func connectionCount() {
        let twCount = TwitterApi.sharedManager.followerCount
        let fbCount = FaceBookApi.sharedManager.friendCount
        return twCount + fbCount
    }
}

The connectionCount() function returns the total number of connections on FaceBook and Twitter, using the singleton managers to access those two different APIs. But if we just look at the API, it’s not clear that SocialInfo depends on FaceBookApi and TwitterApi. For all we know, it’s valid to write

let count = SocialInfo().connectionCount()

but looking at the code for that class, we can tell that TwitterApi and FaceBookApi need to be set up and configured first. Imagine if this were a closed source library. We would have no way to know about these implementation details. And we shouldn’t have to care about implementation details anyway.

Now think about what it means to test the SocialInfo class. Our tests shouldn’t rely on network resources, so we’re going to have to mock those singletons. I’ve used several mocking libraries and these are always finicky at best. In Swift especially, the mocking libraries are quite young and this sort of thing is still difficult.

Now let’s consider the same class, rewritten a little for DI.

class SocialInfo {
    let twApi: TwitterApi
    let fbApi: FaceBookApi

    init(twitterApi: TwitterApi, facebookApi: FaceBookApi) {
        twApi = twitterApi
        fbApi = facebookApi
    }

    func connectionCount() {
        let twCount = twApi.followerCount
        let fbCount = fbApi.friendCount
        return twCount + fbCount
    }
}

With the updates, it’s clear that SocialIinfo depends on a valid instance of TwitterApi and FaceBookApi. Getting a connection count looks like

let count = SocialInfo(twitterApi, facebookApi).connectionCount()

It’s clear that we need to have a configured instance of the Twitter and Facebook API objects ready to pass in so that SocialIinfo can use them to connect with the services.

And testing is much easier too! Instead of having to build mock objects when can create FakeTwitterApi and FakeFaceBookApi objects that are subclasses of our real objects and just hardwired to return known values for the counts. This is really straightforward, readable, and doesn’t require any mocking.

So that’s dependency injection — simply the idea that dependencies are passed in explicitly rather than pulled out of the ether via some magical singleton or (gasp!) global variable.

Tune in next time for the exciting conclusion and the implementation of pragmatic dependency injection in Swift.