iOS writing unit test involving Mknetworkkit - ios

I tried to run MKNetworkKit in my project, it seemed to be fine. I then created a unit test project and call the engine to make some requests (using the MkNetworkOperation). I get no apparent error but the completionhandler block never get called, and so was the errorblock.
I examined my unit test setup, there's no apparent error, and the whole thing "ran". I just dont get any response. If i switch and do this in my main project, it works.
I have also added all the necessary framework to the unit test project.
One thing i notice is that Reachability message get printed out for the case that worked, but nothing for the case that didnt.
Any clue as to whats going on?

Sounds like you need a semaphore or something similar to block the execution until the request returns. Unit tests run until the end of the method. If they hit the end of the method without an error, they were successful. Since your using MKNetworkKit, the server request is async, so the end of the method is hit before the request is complete. I found a helper class to help with unit tests. I'll try to find it again and link to it with some sample code.
Found it: http://www.touch-code-magazine.com/unit-testing-for-blocks-based-apis/
So it's been a while since I wrote these tests. I'm pretty sure this will work, but you might have to tweak it.
-(void)tests {
//setup
NSString *key = #"myTestKey";
//test
STAssertNoThrow(
[API resetPassword:#"fakeemail#this.net callback:^(NSDictionary *result) {
STAssertNotNil(result, #"reset pass response was nil"]);
[[TestSemaphor sharedInstance] lift:key];
} error:^(NSDictionary *error) {
STFail(#"reset password failed: %#", error.description);
[[TestSemaphor sharedInstance] lift:key];
}], #"reset password failed");
[[TestSemaphor sharedInstance] waitForKey:key];
}

Related

Wait for XCTestExpectation before starting next unit test in XCode

I have a number of asynchronous unit tests which work correctly on their own using expectations. However when I run all the tests in the suit, they do not wait for each other to complete - the asynchronous callbacks are still pending when the next tests start running. What I want is for each test to wait for the expectations in the previous test before running. These tests use a shared database, so having them overlap leads to annoying additional complexity, and failing tests when run as a suite.
- (void)testSignIn {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
XCTestExpectation *expectation =
[self expectationWithDescription:#"Expectations"];
[_userManager signInWithUsername:kUserEmail andPassword:kUserPassword
success:^{
XCTAssertNotNil([_userManager getCurrentUser]);
XCTAssertNotNil([_userManager getCurrentUser].plan);
XCTAssertTrue([_userManager getCurrentUser].plan.liveStream == TRUE);
[expectation fulfill];
} failure:^(EDApiError *apiError) {
XCTAssertTrue(FALSE); // Should not fail
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) {
if (error) {
NSLog(#"Timeout Error: %#", error);
}
}];
}
Use XCTWaiter or waitForExpectations(timeout:handler:) to stall the completion of each test until the expectations have been fulfilled.
This blog post explains some of the more complex pitfalls you might come across when writing asynchronous tests and how to prevent them: https://jeremywsherman.com/blog/2016/03/19/xctestexpectation-gotchas/
I've found you need to fulfil expectations on the main thread - important if your async completion block is maybe running on another thread. I've also found that the problem can be triggered by a faulty test that ran earlier, so it isn't always caused by the test the failure manifests in.
Also, watch if you're spawning lots of async blocks (I do this for crazy thread safety tests, or tests checking exclusive access to a resource is working as expected, and in the order expected). Once you fulfil the expectation, the testing moves on to the next test, but if you've fired off a lot of async blocks after that point, they're maybe still running.

Parse PFAnonymousUtils logInWithBlock not returning completion block when no internet connection

I use Parse SDK 1.6.2 in my app. I'm trying to create an anonymous user using:
[PFAnonymousUtils logInWithBlock:^(PFUser *user, NSError *error) {
if (error) {
NSLog(#"Anonymous login failed.");
} else {
NSLog(#"Anonymous user logged in.");
}
}];
If there is internet everything works fine, the completion block gets called and one of the NSLogs will be called. HOWEVER if there is no internet, the completion block NEVER gets called, the debugger will break somewhere in the Parse library but not crash. The console outputs "+[PFObject parseClassName]: unrecognized selector sent to class" but doesn't crash and instead just never calls the completion block and continues running. I think a workaround would be to check internet connection first but wondering if I'm missing something. Hector Ramos are you out there?
There is a difference between users created by [PFAnonymousUtils logInWithBlock...] and [PFUser enableAutomaticUser]. If you need unconditionally created anonymous user (including "no internet" conditions), use the second method. You can read details here: http://blog.parse.com/2012/04/02/protect-user-data-with-new-parse-features/

IBM Worklight 6.2 ChallengeHandler submitFailure: doesn't behave as expected

I am using Worklight 6.2 iOS native framework. I have implemented a custom MyChallengeHandler that subclasses ChallengeHandler, and logging in:
[[WLClient sharedInstance] login:#"SomeRealm" withDelegate:LoginListener];
In ChallengeHandler I can submit success, which calls onSuccess: in LoginListener:
[self submitSuccess:response];
However, I can't submit failure, which I was expecting to call onFailure: in LoginListener. In fact, it looks like there is no effect when calling submitFailure: and onFailure: never gets called in LoginListener.
Also, I don't see a declaration of submitFailure: in WL ChallengeHandler header, it is available only in BaseChallengeHandler.
My main point is, currently it looks like the LoginListener onFailure: method is never called, but there are cases when the handleChallenge: should fail. And LoginListener should get released.
Is this a known issue in Worklight, is there any workaround?
Update 1:
Just found a similar problem for JS client API, which isn't useful for native:
Adapter procedure call, reporting an authentication failure
Update 2:
This might be important. I am using Adapter authentication, but in the IBM example there is submitLoginForm:, maybe my issue is for Adapter authentication only.
[self submitAdapterAuthentication:invocationData options:nil]
It's a big vague so far but I will write some possibilities I think of. Let me know if any help, and I will update my answer.
1) Your code above says you use the class ChallangeHandler. I am not sure if the typo is only in StackOverflow or in your real code as well, but the class is ChallengeHandler, with a e.
Same for handleChallange instead of handleChallenge.
2) Did you register your challenge handler somewhere?
[[WLClient sharedInstance] registerChallengeHandler:[[MyChallengeHandler alloc] initWithViewController:self] ];
3) Do you ever get to the success of your LoginListener? And of your challenge handler?
You are correct that the documentation does not mentiond submitFailure - I will look into that.
However in code I wrote, I was able to use this without problems.
#implementation MyChallengeHandler
//...
-(void) onSuccess:(WLResponse *)response {
NSLog(#"inside challenge success");
[self.vc.navigationController popViewControllerAnimated:YES];
[self submitSuccess:response];
}
-(void) onFailure:(WLFailResponse *)response {
NSLog(#"inside challenge failure");
[self submitFailure:response];
}

Firebase withCompletionBlock not called if there is no connection

I am using the following:
Firebase *fb =[[Firebase alloc] initWithUrl:url];
[fb setValue:d withCompletionBlock:^(NSError *error, Firebase *ref) {
if (error) {
// bad news
} else {
}
}];
This seems to work great IF you have a connection, if not it seems the callback is never called. If that is the case do I then need to wrap this whole thing in a connectedRef? Seems like alot of extra work when I would guess the completion block would just fail with an error status of not online.
Anyone else having this issue?
The idea behind Firebase is it synchronizes data for you. It's more than just a simple request / response system. So if you do a setValue while offline, Firebase will hold onto that data until you are online, and then it'll do the setValue at that time (and then the completion block will be called).
So the behavior you're seeing is expected. If you only want to do the setValue if you're online, then yes, you'll need to use a .info/connected observer. But you could still run into issues if for instance you go offline at the moment you try to do the setValue or something along those lines. In general it's better to just do the setValue and let Firebase take care of it for you.

Delayed OCMock verify / Dealing with Timeout in Unit Tests

I'm testing real web service calls with OCMock.
Right now I'm doing something like:
- (void)testWebservice
{
id mydelegatemock = [OCMockObject mockForProtocol:#protocol(MySUTDelegate)];
[[mydelegatemock expect] someMethod:[OCMArg any]];
[SUT sutWithDelegate:mydelegatemock];
// we need to wait for real result
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
[(OCMockObject*)mydelegatemock verify];
}
It works fine, but it implies that every such test will take 2 seconds.
Is there a way I can set a timeout of e.g. 2 seconds, and let a call to someMethod of mydelegatemock immediately verify and complete the test case?
I do this using a handy utility function I found at this link:
#import <Foundation/Foundation.h>
#import <OCMock/OCMock.h>
#interface TestUtils : NSObject
+ (void)waitForVerifiedMock:(OCMockObject *)mock delay:(NSTimeInterval)delay;
#end
And the implementation:
#import "TestUtils.h"
#implementation TestUtils
+ (void)waitForVerifiedMock:(OCMockObject *)inMock delay:(NSTimeInterval)inDelay
{
NSTimeInterval i = 0;
while (i < inDelay)
{
#try
{
[inMock verify];
return;
}
#catch (NSException *e) {}
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
i+=0.5;
}
[inMock verify];
}
#end
This allows me to to wait up to a maximum delay (in seconds) without waiting the full amount each time.
I would separate the functional testing of your web services (if you need to do that at all) from the unit testing of your class that processes the web service result.
To unit test, you should mock the web service call, providing a mock result. Then your test would verify that, for that well-defined result, your class behaves accordingly.
If you also want to do functional testing of your web service (say that it returns a specific response given some request), you don't need to mock anything--just call the service and make assertions on the result.
By separating out your tests, you have finer control over the test runs. For example, you could run your fast-running unit tests every time you change code, but run your slow-running functional tests nightly, on a dedicated server, or as needed. And when a test breaks, you'll know whether it's your code or something wrong with the web service.
You could also switch to GHUnit, like a fellow member suggests in the answer to this related question:
SenTestingKit in Xcode 4: Asynchronous testing?
You can find GHUnit here
https://github.com/gabriel/gh-unit

Resources