Shock 4.0.0

Shock 4.0.0

Maintained by Mark Cousins, Sneha Narayana Swamy, Alberto De Bortoli.



 
Depends on:
Swifter~> 1.4.7
GRMustache~> 7.3.2
 

Shock 4.0.0

Shock

Build Status Version License Platform

A HTTP mocking framework written in Swift.

Summary

Shock lets you quickly and painlessly provided mock responses for web requests made by your iOS app.

This is particularly useful when writing both unit and UI tests where you often want to receive staged responses to allow reliable testing of all of your features.

It also provides an alternative to hitting live APIs and avoids the uncertainty of receiving unexpected failures or response content as a result.

When used with UI tests, Shock runs its server within the UI test process and stores all its responses within the UI tests target - so there is no need to pollute your app target with lots of test data and logic.

Installation

Add the following to your podfile:

pod 'Shock', '~> x.y.z'

You can find the latest version on cocoapods.org

How does it work?

Shock aims to provide a simple interface for setting up your mocks.

Take the example below:

class HappyPathTests: XCTestCase {

    var mockServer: MockServer!

    override func setUp() {
        super.setUp()
        mockServer = MockServer(port: 6789, bundle: Bundle(for: type(of: self)))
        mockServer.start()
    }

    override func tearDown() {
        mockServer.stop()
        super.tearDown()
    }

    func testExample() {

        let route: MockHTTPRoute = .simple(
            method: .get,
            urlPath: "/my/api/endpoint",
            code: 200,
            filename: "my-test-data.json"
        )

        mockServer.setup(route: route)

        /* ... Your test code ... */
    }
}

Bear in mind that you will need to replace your API endpoint hostname with 'localhost' and the port you specify in the setup method during test runs.

e.g. https://localhost:6789/my/api/endpoint

In the case or UI tests, this is most quickly accomplished by passing a launch argument to your app that indicates which endpoint to use. For example:

let isRunningUITests = ProcessInfo.processInfo.arguments.contains("UITests")
if isRunningUITests {
    apiConfiguration.setHostname("http://localhost:6789/")
}

Shock Route Tester

Example app screenshot

The Shock Route Tester example app lets you try out the different route types. Edit the MyRoutes.swift file to add your own and test them in the app.

Route types

Shock provides different types of mock routes for different circumstances.

Simple Route

A simple mock is the preferred way of defining a mock route. It responds with the contents of a JSON file in the test bundle, provided as a filename to the mock declaration like so:

let route: MockHTTPRoute = .simple(
    method: .get,
    urlPath: "/my/api/endpoint",
    code: 200,
    filename: "my-test-data.json"
)

Custom Route

A custom mock allows further customisation of your route definition including the addition of query string parameters and HTTP headers.

This gives you more control over the finer details of the requests you want your mock to handle.

Custom routes will try to strictly match your query and header definitions so ensure that you add custom routes for all variations of these values.

let route = MockHTTPRoute = .custom(
    method: .get,
    urlPath: "/my/api/endpoint",
    query: ["queryKey": "queryValue"],
    headers: ["X-Custom-Header": "custom-header-value"],
    code: 200,
    filename: "my-test-data.json"
)

Redirect Route

Sometimes we simply want our mock to redirect to another URL. The redirect mock allows you to return a 301 redirect to another URL or endpoint.

let route: MockHTTPRoute = .redirect(
    .redirect(urlPath: "/source", destination: "/destination")
)

Templated Route

A templated mock allows you to build a mock response for a request at runtime. It uses Mustache to allow values to be built in to your responses when you setup your mocks.

For example, you might want a response to contain an array of items that is variable size based on the requirements of the test.

Check out the /template route in the Shock Route Tester example app for a more comprehensive example.

let route = MockHTTPRoute = .template(
    method: .get,
    urlPath: "/template",
    code: 200,
    filename: "my-templated-data.json",
    data: [
        "list": ["Item #1", "Item #2"],
        "text": "text"
    ])
)

Collection

A collection route contains an array of other mock routes. It is simply a container for storing and organising routes for different tests. In general, if your test uses more than one route

Collection routes are added recursively, so a given collection route can be included in another collection route safely.

let firstRoute: MockHTTPRoute = .simple(method: .get, urlPath: "/route1", code: 200, filename: "data1.json")
let secondRoute: MockHTTPRoute = .simple(method: .get, urlPath: "/route2", code: 200, filename: "data2.json")
let collectionRoute: MockHTTPRoute = .collection(routes: [ firstRoute, secondRoute ])

Timeout Route

A timeout route is useful for testing client timeout code paths. It simply waits a configurable amount of seconds (defaulting to 120 seconds). Note if you do specify your own timeout, please make sure it exceeds your client's timeout.

let route: MockHTTPRoute = .timeout(method: .get, urlPath: "/timeouttest")
let route: MockHTTPRoute = .timeout(method: .get, urlPath: "/timeouttest", timeoutInSeconds: 5)

Force all calls to be mocked

In some case you might prefer to have all the calls to be mocked so that the tests can reliably run without internet connection. You can force this behaviour like so:

server.forceAllCallsToBeMocked()

Your tests will hit an assert if any call wasn't mocked.

License

Shock is available under Apache License 2.0. See the LICENSE file for more info.