Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't get OHHTTPStubs to work with NSURLSession (Bug?) #38

Closed
Goles opened this issue Oct 11, 2013 · 16 comments
Closed

Can't get OHHTTPStubs to work with NSURLSession (Bug?) #38

Goles opened this issue Oct 11, 2013 · 16 comments

Comments

@Goles
Copy link
Contributor

Goles commented Oct 11, 2013

I'm trying to use OHHTTPStubs in my XCTest class,

This is how I configured OHTTPStubs in my test file.

//
// Tests Configuration
//
- (void)setUp
{
    [super setUp];
    _bundle = [NSBundle bundleForClass:[self class]];
    [self configureHTTPStubs];
    [self installHTTPStubs];
}

- (void)configureHTTPStubs
{
    [OHHTTPStubs onStubActivation:^(NSURLRequest *request, id<OHHTTPStubsDescriptor> stub) {
        NSLog(@"[OHHTTPStubs] Request to %@ has been stubbed with %@", request.URL, stub.name);
    }];
}

- (void)installHTTPStubs
{
    HIAPIRequests *requester = [[HIAPIOperator sharedOperator] requester];
    [OHHTTPStubs setEnabled:YES forSessionConfiguration:requester.session.configuration];
    [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
        return [request.URL.path isEqualToString:@"/image_upload"];
    } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) {
        return [[OHHTTPStubsResponse responseWithFileAtPath:OHPathForFileInBundle(@"image_upload_ws_response.json", nil)
                                                 statusCode:201
                                                    headers:@{@"Content-Type":@"text/json"}] responseTime:OHHTTPStubsDownloadSpeed3G];
    }].name = @"Image Upload OK";

}

//
// In my Requester class this is how I setup the NSURLSession configuration
//
- (void)configureURLSession
{
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    _session = [NSURLSession sessionWithConfiguration:config];
}

And this is how I'm performing a request

- (void)uploadImage:(UIImage *)image
    completionBlock:(operationCompletionBlock)completionBlock
      progressBlock:(operationProgressBlock)progressBlock
{
    NSData *imageData = UIImageJPEGRepresentation(image, 0.80);
    NSURLRequest *request = [NSURLRequest requestWithURLString:@"/image_upload"];
    NSURLSessionUploadTask *uploadTask = [_session uploadTaskWithRequest:request
                                                            fromData:imageData
                                                   completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                       completionBlock(data, error);
                                                   }];

    [_progressTable setObject:progressBlock forKey:uploadTask];
    [uploadTask resume];
}

In the completionHandler callback I'm basically getting a no domain found error (error NSURLError * domain: @"NSURLErrorDomain" - code: -1003 0x08a70740) , @"A server with the specified hostname could not be found."

I'm completely sure that I'm querying the correct URL (the one I stubbed with OHHTTPStubs) in my test.

What could be going on here? Bug maybe?

@AliSoftware
Copy link
Owner

Maybe related to #37?

Are you using CocoaPods to integrate OHHTTPStubs?
Do you have a complete project to post somewhere so I can have a glimpse at your whole configuration (-ObjC flag and other flags used, integration used, etc?

What is your requester.session.configuration, which kind of NSURLSessionConfiguration does it return, does the swizzling get triggered (it should, and in that case you shouldn't need to call setEnabled:forSessionConfiguration:), are you sure you're not using a backgroundSessionConfiguration, what are the protocolClasses installed, do the NSURLProtocol internal methods get triggered?

Thx

@alvarezloaiciga
Copy link

If you are using cocoapods there is an issue if you have the pod in both targets, so you should probably do:

target :TargetTests, :exclusive => true do
  pod 'OHHTTPStubs'
end

@Goles
Copy link
Contributor Author

Goles commented Oct 11, 2013

@AliSoftware Thanks for the quick reply,

@alvarezloaiciga I'm not using CocoaPods... I'm trying to integrate the library manually. I think the easiest thing is to provide you a link to the project.

You can go to HIAPIOperatorTests.m line 43 and run that test.

You can set a breakpoint in HIAPIRequests line 40 if you want to check the exact server response.

Hope this helps you out :)

@AliSoftware
Copy link
Owner

@alvarezloaiciga Are you talking about an issue in CocoaPods, or an issue in OHHTTPStubs, for the case when it is on both targets?

@Goles Thx I'll take a look!

@AliSoftware
Copy link
Owner

@Goles I don't understand your example project/workspace configuration:

  • You have a workspace containing OHHTTPStubs.xcodeproj, that builds the library libOHHTTPStubs.lib
  • You link your Unit Tests bundle to this libOHHTTPStubs.a. Great
  • BUT you also have a group External/OHHTTPStubs in your Example.xcodeproj projects, that only contains OHHTTPStubs.h and OHHTTPStubs.m and not the rest of my library ?! Why do you need adding this to your project in addition of having the lib?

The OHHTTPStubs.m file you added in your Example.xcodeproj/Example/External/OHHTTPStubs group is only linked with your Example application target (and not with the Unit Tests), so that's probably not the cause of the issue regarding stubs not being called in your Unit Tests, but still, this is some strange configuration of your project (that can lead to undefined and strange behavior, not knowing which implementation is used in each circumstance…)

@Goles
Copy link
Contributor Author

Goles commented Oct 11, 2013

Great! Let me know if I can help!

Sent from my iPhone

On Oct 11, 2013, at 2:14 PM, AliSoftware [email protected] wrote:

@alvarezloaiciga Are you talking about an issue in CocoaPods, or an issue in OHHTTPStubs, for the case when it is on both targets?

@Goles Thx I'll take a look!


Reply to this email directly or view it on GitHub.

@AliSoftware
Copy link
Owner

Also you call setEnabled:forSessionConfiguration: too late in your code.

The method [OHHTTPStubs setEnabled:YES forSessionConfiguration:requester.session.configuration]; is called in your code way AFTER you have created your NSURLSession with [NSURLSession sessionWithConfiguration:config] (which is done in your -[HIAPIRequests configureURLSession] method called by its init method

As stated in Apple's NSURLSession and NSURLSessionConfiguration documentation, an NSURLSessionConfiguration is deep-copied by the NSURLSession when it uses it to create the session, so modifying the NSURLSessionConfiguration AFTER creating the NSURLSession with it has no effect on the already-created NSURLSession

Note that there is also a reminder in my own documentation (see the @note in the documentation of my setEnabled:forSessionConfiguration: method) that explains again that you should call this method on the NSURLSessionConfiguration BEFORE creating an NSURLSession with it. So you have no excuses here 😄


In practice, due to my method swizzling that should be magically done by my NSURLSessionConfiguration+OHHTTPStubs category, you shouldn't even have to call setEnabled:forSessionConfiguration: so there is still an issue on my side, I will keep investigating to understand why my method swizzling does not get called. But aside from that, be aware of those issues in your code and your misuse of NSURLSessionConfiguration methods calling order.

@AliSoftware
Copy link
Owner

Another note: your spinRunLoopWithTimeout:predicate: method lacks a XCTFail(@"Timed out") macro somewhere.
If the while loop exists not because of the predicate() turning to YES but because of the timeout being reached, the loop will exit and the test will end… and pass, whereas there has been a timeout and it should fail.

Still not related to the original issue, but I thought you should know and fix your code on this too.

@AliSoftware
Copy link
Owner

@alvarezloaiciga @Goles Please test the new HEAD (latest commit that includes my fix) to validate it correctly fixes your issues

@Goles
Copy link
Contributor Author

Goles commented Oct 11, 2013

Wow you guys are fast, will validate it asap.

@Goles
Copy link
Contributor Author

Goles commented Oct 11, 2013

@AliSoftware It's fixed, working fine now. 👍

@AliSoftware
Copy link
Owner

Great 😃

Note that from now on you won't need to call setEnabled:forSessionConfiguration: at all then. It is automatically done for you by the method swizzling, so you are assured that it is enabled soon enough and more importantly before you create of the NSURLSession with your NSURLSessionConfiguration.

➡️ So you can completely remove your call to setEnabled:forSessionConfiguration from your tests in the project example you gave me above.

The only usage you could have for setEnabled:forSessionConfiguration: would be to turn OFF stubs on a given configuration, BEFORE creating the NSURLSession with that config.

@Goles
Copy link
Contributor Author

Goles commented Oct 12, 2013

@AliSoftware Yeah, I just added the setEnabled:forSessionConfiguration because I was wondering if it was going to be helpful. I cleaned it up.

On a side-note, I was going to add the time-out thing too:

- (void)spinRunLoopWithTimeout:(NSTimeInterval)timeout predicate:(BOOL (^)())predicate
{
    NSDate *future = [NSDate dateWithTimeIntervalSinceNow:timeout];
    while (predicate() == NO && [[NSDate date] earlierDate:future] != future) {
        if ([future timeIntervalSinceNow] <= 0.0f) {
            XCTFail(@"Timeout...");
        }
        CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, YES);
    }
}

I think this is better.

Thanks again for your help!

@AliSoftware
Copy link
Owner

Ok thx.


Regarding your spinRunLoopWithTimeout:predicate: method, that's better but in your code now checks for the timeout twice (once with [[NSDate date earlierDate:future] != future and once with [future timeIntervalSinceNow] <= 0.0f).

I suggest you remove your [[NSDate date] earlierDate:future] != future test in your while condition, as:

  • It is time consuming (you create an NSDate instance with [NSDate date] at each iteration)
  • The while loop will break with an exception as soon as you reach the XCTFail

@Goles
Copy link
Contributor Author

Goles commented Oct 12, 2013

Lol, you're correct, pasted the wrong snippet ;)

@AliSoftware
Copy link
Owner

3.0.1 is out with this fix!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants