Skip to content

Instantly share code, notes, and snippets.

@rbsgn
Last active October 29, 2015 07:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rbsgn/b6d72869870bc2969aa6 to your computer and use it in GitHub Desktop.
Save rbsgn/b6d72869870bc2969aa6 to your computer and use it in GitHub Desktop.
A minimum viable HTTP response stubber
import Foundation
class StubHTTP: NSURLProtocol {
// MARK: - Public Interface
class func stubRequest(requestTest: NSURLRequest -> Bool, withResponse data: NSData) {
stubRequest(requestTest, withResponses: [data])
}
class func stubRequest(requestTest: NSURLRequest -> Bool, withResponses datas: [NSData]) {
self.stub = Stub(requestTest: requestTest, responses: datas)
}
class func stubGETRequest(to urlString: String, withString string: String) {
stubGETRequestTo(urlString, withResponse: string.dataUsingEncoding(NSUTF8StringEncoding)!)
}
class func stubGETRequestTo(urlString: String, withResponse data: NSData) {
let requestFilter: (NSURLRequest) -> Bool = { request in
guard
let URL = request.URL,
let url = NSURL(string: urlString)
else {
return false
}
return url == URL
}
stubRequest(requestFilter, withResponses: [data])
}
class func resetResponseStub() {
stub = nil
timesResponded = 0
}
// MARK: Required methods to override in all NSURLProtocol subclasses
static var stub: Stub?
override class func canInitWithRequest(request: NSURLRequest) -> Bool {
return true
}
override class func canonicalRequestForRequest(request: NSURLRequest) -> NSURLRequest {
return request
}
override func stopLoading() {
}
override func startLoading() {
guard
let client = self.client,
let stub = self.dynamicType.stub,
let requestURL = request.URL
else {
return
}
if stub.requestTest(request) {
let response = NSHTTPURLResponse(URL: requestURL, statusCode: 0, HTTPVersion: nil, headerFields: nil)
client.URLProtocol(self, didReceiveResponse: response!, cacheStoragePolicy: .NotAllowed)
client.URLProtocol(self, didLoadData: stub.responses[self.dynamicType.timesResponded++])
client.URLProtocolDidFinishLoading(self)
}
else {
client.URLProtocol(self, didFailWithError: NSError(domain: ErrorDomain, code: Errors.NoStubForRequest.rawValue, userInfo: nil))
}
}
static var timesResponded = 0
let ErrorDomain = "StubHTTPErrorDomain"
enum Errors: Int {
case NoStubForRequest = -1
}
struct Stub {
let requestTest: NSURLRequest -> Bool
let responses: [NSData]
}
}
import XCTest
class StubHTTPTest: XCTestCase {
let stubbedURL = NSURL(string: "http://example.org")!
let unstubbedURL = NSURL(string: "http://example.com")!
let stubResponse = NSString(string: "foo").dataUsingEncoding(NSUTF8StringEncoding)!
let stubResponse2 = NSString(string: "bar").dataUsingEncoding(NSUTF8StringEncoding)!
var requestFilter: (NSURLRequest -> Bool)! = nil
var waitExpectation: XCTestExpectation! = nil
var urlSession: NSURLSession! = nil
override func setUp() {
super.setUp()
requestFilter = { $0.URL == self.stubbedURL }
waitExpectation = expectationWithDescription("Wait for stubbed response")
let sessionConfiguration = NSURLSessionConfiguration.ephemeralSessionConfiguration()
sessionConfiguration.protocolClasses = [ StubHTTP.self ]
urlSession = NSURLSession(configuration: sessionConfiguration)
}
override func tearDown() {
StubHTTP.resetResponseStub()
super.tearDown()
}
func test_CanStubHTTPRequestResponse() {
StubHTTP.stubRequest(requestFilter, withResponse: stubResponse)
let task = urlSession.dataTaskWithURL(stubbedURL) { data, response, error in
self.waitExpectation.fulfill()
XCTAssertEqual(data, self.stubResponse)
}
task.resume()
waitForExpectationsWithTimeout(0.1, handler: nil)
}
func test_GivenRequestWithoutStubResponse_FailsWithError() {
StubHTTP.stubRequest(requestFilter, withResponse: stubResponse)
let task = urlSession.dataTaskWithURL(unstubbedURL) { data, response, error in
self.waitExpectation.fulfill()
XCTAssertNil(data)
XCTAssertNotNil(error)
}
task.resume()
waitForExpectationsWithTimeout(0.1, handler: nil)
}
func test_CanStubMoreThanOneResponseForRequest() {
StubHTTP.stubRequest(requestFilter, withResponses: [stubResponse, stubResponse2])
let task = urlSession.dataTaskWithURL(stubbedURL) { data, response, error in
XCTAssertEqual(data, self.stubResponse)
let nextTask = self.urlSession.dataTaskWithURL(self.stubbedURL) { data, response, error in
XCTAssertEqual(data, self.stubResponse2)
self.waitExpectation.fulfill()
}
nextTask.resume()
}
task.resume()
waitForExpectationsWithTimeout(0.1, handler: nil)
}
func test_CanStubResponseHTTPRequestResponse_UsingGETRequestShortcut() {
StubHTTP.stubGETRequestTo("http://example.com/", withResponse: stubResponse)
let task = urlSession.dataTaskWithURL(NSURL(string: "http://example.com/")!) { data, response, error in
self.waitExpectation.fulfill()
XCTAssertEqual(data, self.stubResponse)
}
task.resume()
waitForExpectationsWithTimeout(0.1, handler: nil)
}
func test_CanStubGETRequestWithString() {
StubHTTP.stubGETRequest(to: "http://example.com/", withString: "foo")
makeGETRequest("http://example.com/") { data in
self.waitExpectation.fulfill()
XCTAssertEqual(data, "foo".dataUsingEncoding(NSUTF8StringEncoding))
}
waitForExpectationsWithTimeout(0.1, handler: nil)
}
func makeGETRequest(urlString: String, completion: (NSData) -> Void) {
let task = urlSession.dataTaskWithURL(NSURL(string: urlString)!) { data, _, _ in completion(data!) }
task.resume()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment