AliSoftware / OHHTTPStubs

Stub your network requests easily! Test your apps with fake network data and custom response time, response code and headers!
MIT License
5.04k stars 601 forks source link

Can't stub when using swift in test case. #171

Closed jzau closed 8 years ago

jzau commented 8 years ago

I have tried several times of different ways running the same pieces of codes. It works in main project but can't stub any request when it runs in test case.

    let expectation = self.expectationWithDescription("desc")

    let urlString = "https://api.com.au/test"

    stub(isPath("/test")) { _ in
        let stubPath = OHPathForFile("stub.json", self.dynamicType)
        return fixture(stubPath!, headers: ["Content-Type":"application/json"])
            .requestTime(0.0, responseTime:OHHTTPStubsDownloadSpeedWifi)
    }

    AFHTTPSessionManager().GET(urlString, parameters: nil, progress: nil, success: { (task, data) in
        print(data)
        expectation.fulfill()
    }) { (task, error) in
        print(error.description)
    }

    waitForExpectationsWithTimeout(5) { (error) in
        print(error?.description)
    }

Main project is written in Objective-C.

AliSoftware commented 8 years ago

Are you using Hosted Tests (which launch the application target itself before starting the Unit Tests)? If so, be sure that you don't do any AFHTTPSessionManager can in your application:didFinishLaunchingWithOptions: method, because this would be executed before your Unit Tests and more importantly before OHHTTPStubs get loaded in memory by Xcode, and thus the NSURLSession created by AFHTTPSessionManager would be created before OHHTTPStubs could even install the stubbing protocol in it.

AliSoftware commented 8 years ago

See also https://github.com/AliSoftware/OHHTTPStubs/issues/126#issuecomment-145149158 which might be similar to your problem, with detailed explanation on what might be happening in your project and how to solve it.

jzau commented 8 years ago

Thanks for your help.

Actually, I didn't use any AFHTTPSessionManager in main project. I just create a new project without unit test but using Objc, than add unit test target using swift. Do nothing about main project. The test case with above codes still can't stub request.

AliSoftware commented 8 years ago

Very strange. Did you compare your project with the example projects provided in the OHHTTPStubs repo to see what you did different?

In your sample project without AFHTTPSessionManager, what did you use instead? If you used NSURLSession and one of its task… method, didn't you forget to call resume on the task be sure to start the request, i.e. does the request even actually fire to the network if you don't try to stub it?

I even wonder if your initial issue ("it works in main project but not in tests") and the one in your simplified example ("can't stub when I try without AFHTTPSessionManager") might be different problems (e.g. one where your test project might be misconfigured and the other where you didn't even start the request, as I too often forget to call resume)

Did you try to place some breakpoints in the OHHTTPStubs library code itself to see if it was even called, especially if it intercepted any request at all to test it against the isPath("/test") and thus if what is wrong is the global setup of OHHTTPStubs or if it's just the test isPath("/test") that is checked but returns false, or whatnot?

I can't really see what's wrong without seeing your sample project anyway, because such code works in my example projects here. Maybe if you provide your sample project I could look at it and check.

jzau commented 8 years ago

I find that it can't stub request even when using NSURLSession in test target. But it works with NSURLConnect.

I tried to use breakpoints in OHHTTPStubs library code in OHHTTPStubsSwift.swift file. I separate isExtension for multiple lines so that it's easy to add breakpoint like this

public func isExtension(ext: String) -> OHHTTPStubsTestBlock {
return {
    req in
    req.URL?.pathExtension == ext
  }
}

but it only stops in return line.

Here is an example project. https://github.com/jackyzh/stubExample.

And I find that it works with NSURLSession.sharedSession.

AliSoftware commented 8 years ago

I haven't looked at your sample project yet, but, if it works with NSURLConnection and NSURLSession.sharedSession (which use NSURLProtocol installed globally) but doesn't work with any other NSURLSession (which are created from NSURLSessionConfiguration objects), that means that you created your NSURLSession before OHHTTPStubs had a chance to load into memory and inject itself in NSURLSessionConfiguration to swizzle its custom NSURLProtocol in there. Which is exactly what issues like #126 are about.

Try to make sure that OHHTTPStubs is loaded in memory before you even create your NSURLSession(configuration:…) so it has a chance to inject itself and intercept future requests. As explained in the Apple doc, once an NSURLSession is created, its internal NSURLSessionConfiguration is frozen and can't be modified, and this also means that OHHTTPStubs can't inject itself there (which it does when the framework is loaded in memory, so usually as soon as the test target is loaded) which it's too late for any NSURLSession created before that (like ones created in applicationDidFinishLaunching, as explained in #126).

I'll take a look at your sample project in a second to see if that is exactly the case you're encountering or not and get back to you to confirm.

AliSoftware commented 8 years ago

Ohhhh wait, just opened your sample project now… The problem is wayyyy simpler than all that! (Sorry for the misdirection in the previous comment)

You just forgot to include the subspec to add support for NSURLSession in your Podfile, so no wonder it doesn't work with NSURLSession :wink:

In fact, the root spec pod 'OHHTTPStubs' contains already the most-often used subspecs, including OHHTTPStubs/Core, OHHTTPStubs/NSURLSession, OHHTTPStubs/JSON and OHHTTPStubs/OHPathHelpers. This means that if you put pod 'OHHTTPStubs' in your Podfile you will already have them all and will then only opt-in for pod 'OHHTTPStubs/Swift' for Swift support.

But if you specifically ask for subspecs only and not for the root pod — as you did in your Podfile as you listed pod 'OHHTTPStubs/Swift and pod 'OHHTTPStubs/OHPathHelpers' but not pod 'OHHTTPStubs' — then it only pick the specific subspecs you specify. So in that case you'll have to opt-in for the OHHTTPStubs/NSURLSession subspec explicitly too.

The solution is:

AliSoftware commented 8 years ago

Tested locally by adding pod 'OHHTTPStubs/NSURLSession' in your Podfile, running pod install then running the tests again, I can definitively confirm this was the reason for the issue.

Closing the issue now (but feel free to add comments here if you have remarks or don't manage to make it work in your end)