Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I mock an Alamofire response only?

I have Swift 2 code using Alamofire that queries a remote URL that it expects to return JSON. I've done enough interactive testing to be reasonably confident that it behaves correctly enough. Now I want to unit-test it so I can rely on this code and then move onto other things.

I'm new to Swift, although I've been programming OO Perl for decades, so there's probably something that I've missed.

Here's how I've set up the checking code so far (unimportant things such as how I get a username and password removed to avoid yak-shaving). The calling code specifies a username and password, and expects one of two callbacks to be called whenever a result is obtained. (I didn't get as far as dealing with errors, so please don't get side-tracked on that subject.)

struct checkUsernameHandler {
    var onSuccess: () -> ()
    var onFailure: () -> ()
}
struct checkUsernameOutput {
    var authenticated: Bool?
}

func checkUsername(user: User, handler: checkUsernameHandler) {
    Alamofire.request(.PUT, NSURL(string: baseURL + "/user/verify?key=" + apiKey)!,
        parameters: [
            "username": user.username!,
            "password": user.password!,
        ],
        encoding: .JSON
    ).responseJSON { response in
        let checkusernameOutput = self.analyseCheckUsernameResponse(response)
        if let authenticated = checkusernameOutput.authenticated {
            if authenticated {
                handler.onSuccess()
            } else {
                handler.onFailure()
            }
        }
    }
}

func analyseCheckUsernameResponse (response: Response<AnyObject, NSError>) -> checkUsernameOutput {
    var output = checkUsernameOutput()
    if (response.result.isSuccess) {
        if let JSON = response.result.value as? NSDictionary {
            // ...
        }
    } else {
        print("Didn't get proper JSON or something: \(response.result.error)")
    }
    return output
}

What I'm looking for is a way of taking raw JSON and passing it to Alamofire somehow, so I can generate a response object that has the appropriate things in it, including errors if it didn't get anything (timeout?) or the data returned was invalid (e.g. an HTML-format 404 or 500 page).

I'm happy enough at the moment with function checkUsername, so I'm not interested in anyone saying "use OHHTTPStubs". I'm looking at testing only the code that I'm uncertain about, which is the code I just wrote.

EDIT: Having faffed about some more, it looks like Alamofire's internals are sufficiently connected that to create a fake Response, I need to do pretty much all of the work involved in setting up a network connection anyway. So perhaps the answer is "it's too much hassle to do that"?

like image 670
Sam Kington Avatar asked Feb 02 '16 03:02

Sam Kington


1 Answers

I had success by overriding the URLProtocol's startLoading method. You can find an example of this within Alamofire's test directory under URLProtocolTests.swift.

By intercepting this call, one could capture the incoming request then parse the URL to match your chosen mock response. Below is an example of how I handle the response:

override func startLoading() {
    //Seperate module I used to handle the request
    let data:NSData = mocks.find(request) as! NSData

    let client = self.client
    let response = HTTPURLResponse(url: (request.url)!, statusCode: 200, httpVersion: "HTTP/1.1", headerFields: cannedHeaders)

    client?.urlProtocol(self, didReceive: response!, cacheStoragePolicy: URLCache.StoragePolicy.notAllowed)
    cannedResponse = data
    client?.urlProtocol(self, didLoad: cannedResponse as! Data)
    client?.urlProtocolDidFinishLoading(self)

    activeTask?.resume()
}

I posted a full example of how to do this on github here: https://github.com/ksteigerwald/MockAlamofire

like image 51
velaru Avatar answered Oct 03 '22 18:10

velaru