OHHTTPStubs not stubbing host?

3.1k Views Asked by At

OHHTTPStubs not mocking requests if the iOS Application has it's own requests?

If the App does any Alamofire request before the tests run, OHHTTPStubs will not mock any requests in unit tests

  • OHHTTPStubs 4.3.0 with swift support
  • Alamofire 2.0.2

Test File - works as expected

stub(isHost("httpbin.org")) { _ in
    let stubData = "Hello World!".dataUsingEncoding(NSUTF8StringEncoding)
    return OHHTTPStubsResponse(data: stubData!, statusCode:200, headers:nil)
}

print("@@Start")
Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
    .responseString() { request, response, data in
        print(data)
}

But once you add the following to AppDelegate.swift, it no longer works and instead makes an actual HTTP request to the service

Alamofire.request(.GET, "http://httpbin.org/get")
    .responseString() { request, response, data in
            print(data)
}

Original question left for posterity

Update: I tried again in a clean project and it worked as expected so there must be something else going on.

In the latest upgrade to swift 2.0, OHHTTPStubs has not been stubbing the requests, and they're instead being passed through and called live.

I set up a short example in a unit test, but it's still passing through. What am I missing?

import Foundation
import Quick
import Nimble
import Alamofire
import OHHTTPStubs

class RequestsSpec: QuickSpec {
var data:Result<String>? = nil

override func spec() {
    describe("the request") {
        it("needs to make a request") {

            stub(isHost("httpbin.org")) { _ in
                let stubData = "Hello World!".dataUsingEncoding(NSUTF8StringEncoding)
                return OHHTTPStubsResponse(data: stubData!, statusCode:200, headers:nil)
            }

            Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
                .responseString() { request, response, data in
                    print(request)
                    print(response)
                    print(data)
                    print(data.value)
                    self.data = data
            }
            expect(self.data).toEventuallyNot(beNil(), timeout: 3)
        }
    }
}

I get

Optional(<NSMutableURLRequest: 0x7f91598e22e0> { URL: http://httpbin.org/get?foo=bar })
Optional(<NSHTTPURLResponse: 0x7f915da8c220> { URL: http://httpbin.org/get?foo=bar } { status code: 200, headers {
    "Access-Control-Allow-Credentials" = true;
    "Access-Control-Allow-Origin" = "*";
    Connection = "keep-alive";
    "Content-Length" = 364;
    "Content-Type" = "application/json";
    Date = "Thu, 01 Oct 2015 04:19:42 GMT";
    Server = nginx;
} })
Optional("{\n  \"args\": {\n    \"foo\": \"bar\"\n  }, \n  \"headers\": {\n    \"Accept\": \"*/*\", \n    \"Accept-Encoding\": \"gzip;q=1.0,compress;q=0.5\", \n    \"Accept-Language\": \"en-US;q=1.0\", \n    \"Host\": \"httpbin.org\", \n    \"User-Agent\": \"ios-consumer-app/com.dante.ouli2 (28; OS Version 9.0 (Build 13A340))\"\n  }, \n  \"url\": \"http://httpbin.org/get?foo=bar\"\n}\n")

Instead of Hello World!

I tried making the request with NSURLSession and OHHTTPStubs worked correctly. Looks like there's something wrong with the OHHTTPStubs/Alamofire interaction?

            let url = NSURL(string: "http://httpbin.org/get")

            let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
                print(NSString(data: data!, encoding: NSUTF8StringEncoding))
                self.data2 = data
            }
  • OHHTTPStubs 4.3.0 with swift support
  • Alamofire 2.0.2
2

There are 2 best solutions below

0
On BEST ANSWER

Opened a ticket with the OHHTTPStubs. It's an edge case between Alamofire and OHHTTPStubs

https://github.com/AliSoftware/OHHTTPStubs/issues/126 Resolution is in the last two posts at the bottom

Some ideas to fix your edge case:

  • Switch to non-hosted tests, so that your App doesn't get launched before your test bundle is loaded
  • Keep hosted tests, but add OHHTTPStubs to your app bundle instead of your test bundle, so that it's loaded as soon as the app is launched when running your tests
  • Hack your AppDelegate code somehow, so that when launched from the tests, it doesn't make any call to Alamofire.request, and that the first request made by Alamofire in the app or in the test (thus triggering its call to Manager.sharedInstance that creates its internal NSURLSessionConfiguration) is only done after the test bundle has been loaded and injected into your app bundle.

And what I ultimately did

I've made it so Alamofire requests aren't made during startup

1
On

When you run Application/UI tests Xcode launches the application in the Simulator, loads the Test bundle as sort of a plugin, then executes the Unit Tests inside the application.

The actual App and the Test bundle are both loaded in memory. If you linked OHHTTPStubs with both the App target and the Test target, the library will be loaded twice and will have two instances loaded in memory. This is causes the stubs in your Test bundle not being able to intercept the request inside the App.

If possible avoid linking OHHTTPStubs to the test target (your App) so that it only loads once.

If you really need to have OHHTTPStubs inside the test target you can append a String that acts like a flag to launchEnvironment during test setup. Then check inside your App if the flag exists via NSProcessInfo.processInfo().environment["SOME_STRING_FLAG"].

For more information regarding this read AliSoftware's wiki on this issue: https://github.com/AliSoftware/OHHTTPStubs/wiki/A-tricky-case-with-Application-Tests