How to load nil Data with a URLProtocol subclass?

806 Views Asked by At

I'm currently trying out a method of stubbing my network requests without needing to mock URLSession by using URLProtocol.

So far it works pretty well and functions by calling self.client?.urlProtocol(_ URLProtocol, didLoad: Data). However I'm unable to pass nil so I can test how my networking class handles being sent a successful response with no data (i.e. a HTTP 204 code). By default URLProtocol returns an empty Data object rather than nil so this does appear to be something I have to set manually.

Is there a way of returning a successful response with nil Data using a URLProtocol subclass? After searching the documentation I can't find anything that jumps out although it seems likely this is possible.

URLProtocolStub

// Custom URLProtocol to be used in place of Apple's HTTP protocols when
// testing network code
class URLProtocolStub: URLProtocol {
    static var testURLs = [URL?: Data]()

    override class func canInit(with request: URLRequest) -> Bool {
        return true
    }

    override class func canonicalRequest(for request: URLRequest) -> URLRequest {
        return request
    }

    override func startLoading() {
        if let url = request.url,
            let data = Self.testURLs[url] {
            self.client?.urlProtocol(self, didLoad: data)
        }
        client?.urlProtocolDidFinishLoading(self)
    }

    override func stopLoading() {}
}

Code to be tested

func requestCards(completionHandler:@escaping (Result<[Card],NetworkCallError>) -> Void ) {
    session.dataTask(with: URL(string: "https://deckofcardsapi.com/api/deck/new/draw/?count=52")!) { data, response, error in
        guard let data = data else {
            completionHandler(.failure(.noData))
            return
        }
1

There are 1 best solutions below

2
Paul F On

Update the definition of testsURLs to allow for the response Data to be nil and call URLProtocolClient.urlProtocol(_:didFailWithError:) when it is.

Your startLoading() implementation would look like this:

static var testURLs = [URL?: Data?]()

override func startLoading() {
    if let url = request.url,
        let data = Self.testURLs[url] {
        self.client?.urlProtocol(self, didLoad: data)
    } else {
        let error = NSError(domain: "", code: 0, userInfo: nil)
        self.client?.urlProtocol(self, didFailWithError: error)
    }
    client?.urlProtocolDidFinishLoading(self)
}

Then add a test case:

myURLProtocolStub.testURLs["http://empty-data.com"] = nil