kinwahlai / YetAnotherHTTPStub

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

YetAnotherHTTPStub

Build Status Carthage compatible https://img.shields.io/badge/paypal.me-donate-blue.svg

In many case, we need to mock the network response when we writing unit test that has network request, like calling an API.

This framework utilizes the URLProcotol to intercept the request and reply the response preset by developer.

This framework is inspired by Mockingjay and Cuckoo

Key Concept

YetAnotherHTTPStub-concept.png

Details

  • There should be only 1 session for each test case
  • You can register as many request as you want for 1 session
  • You can register as many response as you want for 1 request
  • When a session starts, this framework will replace/swizzle the default URLSessionConfiguration creation to inject the custom URLProtocol
  • Also when a session starts, this framework will add the session to XCTestObservationCenter to remove session on testCaseDidFinish

Installation

Carthage

To use YetAnotherHTTPStub with Carthage add in your Cartfile this line:

github "kinwahlai/YetAnotherHTTPStub"

Don't forget to add the Framework into your project.

Example

YetAnotherURLProtocol.stubHTTP { (session) in
    session.whenRequest(matcher: everything)
    .thenResponse(responseBuilder: jsonString("{\"hello\":\"world\"}"))
}

You start a session with YetAnotherURLProtocol.stubHTTP , session.whenRequest to register a request and .thenResponse to register a response.

YetAnotherURLProtocol.stubHTTP { (session) in
    session.whenRequest(matcher:  http(.get, uri: "/login"))
    .thenResponse(responseBuilder: jsonString("{\"success\":true}"))

    session.whenRequest(matcher:  http(.get, uri: "/getContact?page=\\d+&per_page=50"))
    .thenResponse(responseBuilder: jsonString(<Page 1 Response>))
    .thenResponse(responseBuilder: jsonString(<Page 2 Response>))
}

You can also repeat the same response X time by:

YetAnotherURLProtocol.stubHTTP { (session) in
    session.whenRequest(matcher: http(.get, uri: "/polling"))
        .thenResponse(repeat: 2, responseBuilder: jsonString("{\"status\": 0}", status: 200))
        .thenResponse(responseBuilder: jsonString("{\"status\": 1}", status: 200))
}

Now, get notified on response replied by passing a closure to the response

YetAnotherURLProtocol.stubHTTP { (session) in
    session.whenRequest(matcher: http(.get, uri: "/polling"))
        .thenResponse(repeat: 2, responseBuilder: jsonString("{\"status\": 0}", status: 200))
        .thenResponse(configurator: { (param) in
            param.setResponseDelay(2)
                .setBuilder(builder: jsonString("{\"status\": 1}", status: 200))
                .setPostReply {
                    gotNotify = "post reply notification"
                }
        })
}

More examples in ExampleTests, so make sure you go check it out

Matcher

A matcher is a pure function that takes a URLRequest and returns a boolean for checking if the stub request matches the request. And built-in matcher to help simplify the work.

func matcher(request:NSURLRequest) -> Bool {
  return true
}

Built-in matcher

  • everything - Matches everything
  • nothing - Match nothing
  • uri(url/path) - Check if url or path with wildcard matches the request
  • http(method, url/path) - Check if url or path with wildcard and method matches the request

Builder

A builder is another pure function that takes a URLRequest and returns a success or failure response

func matcher(request:NSURLRequest) -> StubResponse {
    let response = NSHTTPURLResponse(URL: request.URL, statusCode: 200, HTTPVersion: nil, headerFields: nil)!
    return .success(response: response, content: .noContent)
}

Built-in builder

  • failure(error)
  • http(status, headers, StubContent)
  • jsonString(string, status, headers)
  • json(body, status, headers)
  • json(data, status, headers)
  • fileContent(filePath, status, headers) - this builder doesnt specific the headers for the mimetype
  • jsonFile(filePath, status, headers) - a wrapper of fileContent that set mimetype to application/json and encoding to utf8

Queue

StubResponse will be replied on a separate DispatchQueue automatically. However, you can specific or switch to use the queue of your choice by using .responseOn(queue: customQueue) function. Response will use the queue from the last response until queue switch by responseOn(queue: customQueue), and then all the subsequent response will use the new queue.

let customQueue = DispatchQueue(label: "custom.queue")
YetAnotherURLProtocol.stubHTTP { (session) in
    session.whenRequest(matcher:  http(.get, uri: "/getContact?page=\\d+&per_page=50"))
    .thenResponse(responseBuilder: jsonString(<Page 1 Response>))
    .responseOn(queue: customQueue)
    .thenResponse(responseBuilder: jsonString(<Page 2 Response>))
}

You can also delay the reply of StubResponse now. The delay value will only apply to the StubResponse that you specify.

let delay: TimeInterval = 5
YetAnotherURLProtocol.stubHTTP { (session) in
    session.whenRequest(matcher:  http(.get, uri: "/status"))
    .thenResponse(withDelay: delay, responseBuilder: jsonString("{\"status\": 1}", status: 200))
}
Checkout the test in Example target for more detail.

Donation

If you like my work on YetAnotherHTTPStub or to support open source, consider a small donation to my bitcoin address or paypal.me:
Donate via Paypal.me

License

MIT License

Copyright (c) 2017 Kin-Wah Lai

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

About

License:MIT License


Languages

Language:Swift 97.3%Language:Ruby 2.1%Language:Objective-C 0.6%