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
- 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
To use YetAnotherHTTPStub with Carthage add in your Cartfile this line:
github "kinwahlai/YetAnotherHTTPStub"
Don't forget to add the Framework into your project.
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
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
}
everything
- Matches everythingnothing
- Match nothinguri(url/path)
- Check if url or path with wildcard matches the requesthttp(method, url/path)
- Check if url or path with wildcard and method matches the request
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)
}
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 mimetypejsonFile(filePath, status, headers)
- a wrapper of fileContent that set mimetype to application/json and encoding to utf8
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))
}
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 |
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.