-
Notifications
You must be signed in to change notification settings - Fork 601
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
Stub not being called #47
Comments
I'm experiencing the same issue with my project and have no idea why. I've NSLog'ed damn near everything and have not figured it out. So I'll contribute what I have since we're having the same issue. iOS7 I've subclassed like this: + (instancetype)sharedClient {
static ROAAPIClient *_sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURL *baseUrl = [NSURL URLWithString:API_BASEURL];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
_sharedClient = [[ROAAPIClient alloc] initWithBaseURL:baseUrl
sessionConfiguration:config];
});
return _sharedClient;
} I've setup my stub like this: [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
NSLog(@"------- %@ %@\n\n", request.URL.host, request.URL.path);
return [request.URL.path isEqualToString:@"/oauth/token"];
} withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) {
return [OHHTTPStubsResponse responseWithFileAtPath:OHPathForFileInBundle(@"successfulClientCredentials.json",nil)
statusCode:200 headers:@{@"Content-Type":@"application/json"}];
}];
I've logged OH hits like this: [OHHTTPStubs onStubActivation:^(NSURLRequest *request, id<OHHTTPStubsDescriptor> stub) {
NSLog(@"Hit stub for requested URL: %@", [request.URL absoluteString]);
}]; I've also included an NSLog in the method of my client class that fires off the HTTP request. It does happen, and the success blocks fire correctly as well. |
I created this demonstration project to maybe help mitigate this efficiently https://github.com/bobbytables/ohhttpstubs_47_bug I'm logging out the response object, which when you run the tests, hits iTunes API (When it should be stubbed to return a json response file). |
@brennon !!! Initialize your client after you setup the stub. Problem solved. |
@brennon I confirm @bobbytables answer : you initialize your client, using an NSURLSession and NSURLSessionConfiguration, before any call to OHHTTPStubs.
But it does this swizzling in its So you have to use the In summary, initializing your client after you setup the stub solves the problem, but you can also still initialize you client before setting up your stub as long as you call any method on the I'm closing this issue as the fix is given in my answer |
Added a TEST #define to my main app target so that in my creation of the AFHTTPSessionManager I now have this:
I've confirmed that
In particular, the first line of the function body calls itself recursively, giving me a stack overflow... Am I missing something? |
In fact, if the pod is compiled into the main target at all (irrespective of whether or not I actually make any calls to OHHTTPStubs), this happens. |
Given what you describe, it seems that the method swizzling is done twice, leading to the Can you place a breakpoint in the I'll try to reproduce the bug on my side and add a protection against that in a future release but have to be sure about the reason first and be sure it will fix the issue for you too, so I'm interested in your feedback and debug informations. And one more question: does this happen only if you have the |
Before I saw your message, I ripped out all the Pods and reinstalled them. Now, I can link against the library built from the Pod with no problem. However, now I am back to my original code. However, now this is the very first line of my app delegate's
Still, my stub (same as above) is not being called. |
I wrote a pared down test to try to get a barebones failing scenario--now I'm back to the recursive swizzle... From app delegate:
Test spec:
Stack trace for
Stack trace for
Stack trace for
Stack trace for
|
Good grief... So, based on the above, it appears that OHHTTPStubs chokes when being asked to swizzle more than one (the one in my subclass of So, I stripped everything down again to this simple test (this is the only one being run in the entire suite:
So, the |
Thanks for all those tests and reports, hopefully it will help me track this nasty issue. I still can't understand how come the I will try and reproduce the issue with the code you provide and investigate further ; unfortunately I am a bit busy lately and can't guaranty you that I'll have time to look into this this weekend. |
@AliSoftware : if a subclass does not implement +initialize but its superclass does, then that superclass’s +initialize will be invoked once per non-implementing subclass and once for itself. You can find the detail. http://www.friday.com/bbum/2009/09/06/iniailize-can-be-executed-multiple-times-load-not-so-much/ |
Interestingly, if I place this test inside my app delegate's
|
For what it's worth, this is all I have to do in a test to descend into recursion hell
Also, if I comment out all mention of OHHTTPStubs in my own code, as confirmed by a workspace-wide find,
Any clues? |
I can confirm that the recursive loop only occurs when the pod is included for my main app target, as well. When it is only linked into the test target, I have no problems. So, now I'm back to my original problem. I can confirm that the stub is called if I initialize my |
@samir2303 I know that but unfortunately this cannot be the reason why the method is swizzled twice, as I already have this kind of protection against That's too bad because that would indeed have been a good lead, so I would have preferred that I forgot about it so that we would have the explanation and solution to this issue… but as the protection is in place, there must be another reason why |
@brennon could you maybe create a small project that highlight the issue and make it available so that we can easily test with the exact same conditions as yours? That would help us speed up the investigation and find what's wrong in Thx |
Here you go: https://github.com/brennon/ohhttpstubs-issue47/. The commented-out test will not run as long as the OHHTTPStub pod is specified for the main target. |
I think I have a lead!
But both calls are from different bundles: one from the application and one from the Test bundle! (that's actually quite what was explain in the comment of the other issue but I didn't quite understood it all the way until then) That's a special case of when you run Unit Tests and made them be hosted by your application:
I don't know if it can be considered as a bug of the iOS Simulator Runtime, as even if that's two different bundles/binary images loading the To avoid that behavior, and thus avoid the To do that:
Note that this is an issue that can happen with any other library/class that declare singletons and/or implement I will keep the issue open as I would like to investigate more on that later, maybe find a way to avoid the swizzling to be done twice if people use their app as the target or their UnitTests instead of "None", and add some warnings about all that in the README, but first we need to understand all that strange behavior better |
@brennon Thanks for the demo projet! But I don't understand how to use it: there is no Podfile in your repository (but there is Also let me know if my solution above (changing the dropdown menu in the General tab of your UnitTest target to "None") solves your issue. |
…oth in the App target and the UnitTest targets, in the case UnitTests use the App as their host app (which is the default for new Xcode5 projects) In such case, two OHHTTPStubs classes where loaded (one in the App Bundle, one in the Test bundle), leading to double swizzling, but only the first OHHTTPStubsProtocol (generally the one loaded by the app bundle) were used, leading to stubs from UnitTest bundle not being called. To fix this, I now: * The swizzling of `NSURLSessionConfiguration` is done using an `NSURLSessionConfiguration` category and its `+load` method to be done only once (much more logical by the way) to avoid double-swizzling (and a callstack overflow) * The insertion of the `OHHTTPStubsProtocol` class in the `protocolClasses` property is done via `[OHHTTPStubs setEnabled:forSessionConfiguration:]` (instead of relying to `objc_getClass()`), to be sure that it uses the `OHHTTPStubsProtocol` class loaded by the current `NSBundle` (and not the one returned by objc_getClass() which is generally the one from the `mainBundle` / App bundle)
Hi again @brennon I just did a commit in a attempt to try and solve this issue at last — even when your UnitTest target uses your app as a host (and loads the OHHTTPStubs twice, once in the app bundle and once in the test bundle), so even in the case you don't apply my suggestion in my comment above. Please tell me if it solves your issue 👍
To try and use it, and confirm that it actually solves your problem, simply change your Podfile to point directly to my repo URL, so that CocoaPods loads the HEAD of my repo (instead of the latest public release):
Then once you do a This mess with the iOS simulator loading two different bundles and thus loading some classes more than once is a tricky one! I hope it solves your issue at last. Please keep me posted! 🍻 |
Sorry about that--I didn't commit add the pod-related files to the repository--have done so now... I can confirm that your changes in your current commit do fix the issues in the test repo that I referenced. I don't know if (but I assume that) they do fix the issue in my 'real' project. This, however, doesn't really address the other issue that I mentioned... Where someone is in the situation that they use a singleton class for NSURLSession-related functionality, and they're doing GUI-related testing in their tests (which I'm doing), is there any way to do so without modifying production code? As it stands now, I have to create a different scheme that allows me to create a different preprocessor |
@brennon are you sure about that? The change I made, namely doing the swizzling in the That way, you can now call The only thing to keep in mind is that when you do GUI-testing — and thus select your app target in the "Target" dropdown menu in the Test Target — and have the
So as a result, all stubs registered in the If you really want to manipulate (disable/remove/…) stubs installed by your application code, so that they don't get in the way of the stubs you create in your test suites, you may use the - (void)setUp
{
// Get the OHHTTPStubs that is loaded in the application's bundle
Class AppOHHTTPStubs = [[NSBundle mainBundle] classNamed:@"OHHTTPStubs"];
// and remove all its stub as we want to use only the stub we will setup in our Test Suite
[AppOHHTTPStubs removeAllStubs];
}
|
I just did the following test that confirms what I just explained:
As a result:
And all that without having to change a thing in the original application's code that you can keep untouched. So that seems to fit the use case you need, right? |
Seems to work now--thanks! |
Cool ! I just released version I will now have to write a dedicated article to explain this tricky case in details, in case other users have the same problem of having to disable the AppDelegate's stubs when running their Application Tests… |
Can you update the article on how to integrate OHHTTPStubs into your Test target and also use it to stub requests from the app target. I'd like to add UISwitches to development builds of my app to turn on the stubbing behaviors when the app is running in the simulator and on a device. I couldn't figure out how to do it from this issue. I would like to do exactly what you're doing in the demo project. But the wiki pages said to NOT link the OHHTTPStubs project to your application target. So there's a gap in the instructions on how to accomplish what the demo project does using the pod file. Thank you, |
@JustinDSN thanks for the reminder, I totally forgot about that article that I had to update. I just wrote every details in it and I hope it covers every use case. In summary:
|
…oth in the App target and the UnitTest targets, in the case UnitTests use the App as their host app (which is the default for new Xcode5 projects) In such case, two OHHTTPStubs classes where loaded (one in the App Bundle, one in the Test bundle), leading to double swizzling, but only the first OHHTTPStubsProtocol (generally the one loaded by the app bundle) were used, leading to stubs from UnitTest bundle not being called. To fix this, I now: * The swizzling of `NSURLSessionConfiguration` is done using an `NSURLSessionConfiguration` category and its `+load` method to be done only once (much more logical by the way) to avoid double-swizzling (and a callstack overflow) * The insertion of the `OHHTTPStubsProtocol` class in the `protocolClasses` property is done via `[OHHTTPStubs setEnabled:forSessionConfiguration:]` (instead of relying to `objc_getClass()`), to be sure that it uses the `OHHTTPStubsProtocol` class loaded by the current `NSBundle` (and not the one returned by objc_getClass() which is generally the one from the `mainBundle` / App bundle)
I already have the host application set to "None" (I'm testing a Framework). I am using a background session, but I am also calling
before setting up any stubs and before initializing the session. Update: I tried changing my session configuration to Either the stubs don't work with background sessions, or my code invalidating the background session on each test isn't working as expected... |
@nicolasmiari-unext Background sessions are handled by the system itself, we can't hook on them as they are performed out-of-process and thus out of reach for interception. See the "Known Limitations" section in the README for more info. |
@AliSoftware Thank you, I missed that. |
I'm setting up a stub in an Specta spec that isn't getting called. Here's the code I'm using:
client
is a singleton instance of anAFHTTPSessionManager
from AFNetworking 2.0. Strangely, this test and other similar tests were passing originally--I don't know what I've done to break them! When the POST request is made, I can check thatOHHTTPStubs.sharedInstance
is the same as when the stub was created. I've added the+[OHHTTPStubs setEnabled:] call per other issues here. The block passed as the
stubRequestsPassingTest:` parameter is never executed--all requests hit the network. Any ideas? Thanks!The text was updated successfully, but these errors were encountered: