Stubbing network requests with fixtures

Developing client and server networking code in lockstep with OHHTTPStubs fixtures. This Swift/Obj-C library provides an easy way to hook into the iOS HTTP networking stack to serve local responses for offline integration and unit testing.


You'd be hard-pressed to find an iOS project that doesn't contain networking code. Saving user data, requesting app content and storing away analytics are just a selection of the activities that require network access.

Developing against the live API is clearly the most desirable, but what if it's still being developed in parallel? Or down for some reason?

Until recently, I would create an alternative implementation and manually stub out the expected API behavior in code. I'd write my unit tests and also inject the stubbed interface into the actual application so I could get ahead with the UI. This approach is uncomplicated and works reasonably well.

Where it breaks down for me is its manual nature. With each revision of the API interface, the stub needs to follow along. And as the stub's implementation expands, this repetitive process grows all the more cumbersome.


OHHTTPStubs' roots lie in Objective-C, but it is perfectly Swift-compatible. When enabled, HTTP responses are intercepted and replaced with predefined fixtures. These typically contain sample output for API calls in JSON, though they can be supplied in any text format.

I have created a basic demo project to demonstrate how this library could be used in your next project. It's all up on GitHub, so feel free to clone/fork it and follow along.


It's an iPhone app which uses Alamofire to call the /ip endpoint of HTTPBin.org. A tap on either button will call the web service and display the response in a UIAlertController.

Here's the implementation for getIPAddress() (AddressHTTPService.swift, line 14):

func getIPAddress(callback: (String) -> ()) {
    Alamofire.request(.GET, "https://httpbin.org/ip").responseJSON { response in
        if let JSON = response.result.value, origin = JSON["origin"] {
            callback(origin as! String)
        }
    }
}

In the same file, I defined a separate class to hold the logic for loading and unloading the fixtures. You're free to choose where you put this code, as it doesn't particularly matter. Wrapping it in a separate class is also optional.

class AddressHTTPServiceStubs {
    static func loadStubs() {
        stub(isPath("/ip")) { request in
            let stubPath = OHPathForFile("ip_response.json", AddressHTTPService.self)
            return fixture(stubPath!, headers: ["Content-Type":"application/json"])
        }
        OHHTTPStubs.setEnabled(true)
    }

    static func unloadStubs() {
        OHHTTPStubs.setEnabled(false)
    }
}

To enable the stubs in your application or tests, simply call the loadStubs() method:

@IBAction func getIpViaStub(sender: UIButton) {
    AddressHTTPServiceStubs.loadStubs()
    self.showIPAddress()
}

The library comes with a couple of built-in request matchers. However, the raw request object is accessible, so more granular filtering shouldn't be too challenging.

That's it!


As you can see it's fairly easy to put stubbed responses in place for offline or unit testing. It's mostly a matter of writing a filter to direct OHHTTPStubs to the correct fixture file, and updating the canned responses whenever an associated API changes.

Credits

Feature image courtesy of Radomir Cernoch, who generously licensed this photo as Attribution-ShareAlike 2.0 Generic. Thank you!