Skip to content

Mocking

Andrew Wagner edited this page Aug 8, 2019 · 5 revisions

You can mock endpoint responses for easy automated testing. There are high-level options.

  • Expectation Setting – Set expectations around how endpoints should be called and define what will be returned from those endpoints.
  • Custom Request Handling – Completely take over the request making process.

Expectation Setting

Expectation setting will be the most common mocking use-case. You start by indicating you would like to mock a particular instance of a web service and then set expectations about what requests should be made. Within those requests, you specify what the endpoint should return as long as the expectation is met.

Start and Stop Mocking

To indicate you want to start mocking a service, call startMocking.

let mock = MyService.shared.startMocking()

This does two important things:

  1. Stops requests from being made for real on this instance and instead requires expectations to be set.
  2. Returns a mock object to allow setting expectations.

Note: When a web service is being mocked, requests made when an expectation is not set, will result in an error. It is not possible for a real request to leak through.

When you are done mocking, you can return to allowing the service to make real requests.

MyService.shared.stopMocking()

Setting Expectations

All expectations are set on the mock object returned from startMocking.

Empty Endpoint Expectation

An empty endpoint can have an expectation that returns any successful or failure response.

// Return success
mock.expect(CheckStatus(), andReturn: .success)

// Return error
mock.expect(CheckStatus(), andReturn: .failure(DecreeError(.unauthorized)))

Note: If the endpoint has a customizable path, this will be validated to match when the request comes in.

In Endpoint Expectations

There are 3 types of expectations for In Endpoints.

// Validate the input exactly matches a fixed input
mock.expect(CreateObject(named: "my object"), receiving: "expected input")

// Throw an error
mock.expect(CreateObject(named: "my object"), throwingError: DecreeError(.unauthorized))

// Custom validation
mock.expect(CreateObject(named: "my object"), validatingInput: { input in
    // Do any custom validation you want on input

    // Return what the endpoint should return
    return .failure(DecreeError(.unauthorized))
})

Note: If the endpoint has a customizable path, this will be validated to match when the request comes in.

Out Endpoint Expectations

Out endpoints are very similar to Empty endpoints except in the success case, you need to return an Output instance.

/// Return a successful output
mock.expect(GetObject(objectId: 2), andReturn: .success(TestObject()))

// Return error
mock.expect(GetObject(objectId: 2), andReturn: .failure(DecreeError(.unauthorized)))

InOut Endpoint Expectations

InOut endpoint expectations are similar to In endpoint expectations except in the success case, you need to return an Output instance.

// Validate the input exactly matches a fixed input and return success
mock.expect(Login(), receiving: .init(username: "user", password: "secret"), andReturn: .success(.init(token: "test-token")))

// Validate the input exactly matches a fixed input and return failure
mock.expect(Login(), receiving: .init(username: "user", password: "secret"), andReturn: .failure(DecreeError(.unauthorized)))

// Throw an error (without expecting any specific input)
mock.expect(Login(), throwingError: DecreeError(.unauthorized))

// Custom validation
mock.expect(Login(), validatingInput: { input in
    // Do any custom validation you want on input

    // Return what the endpoint should return
    return .failure(DecreeError(.unauthorized))
})

Custom Request Handling

Custom request handling is the more advanced form of mocking. It allows you to completely override all response from all requests made to all instances of a web service.

To start handling requests, call the static startHandlingAllRequests handler.

MyService.startHandlingAllRequests(handler: { request in
    return (nil, nil, nil)
})

The handler returns the following three things:

  1. An optional response body.
  2. An optional URLResponse (will usually be an HTTPURLResponse)
  3. An optional error

These are the 3 things that are usually returned from the underlying URLSession data task.

To stop handling requests, simply call stopHandlingAllRequests.

MyService.stopHandlingAllRequests()
You can’t perform that action at this time.