
I was handling a common occurrence when doing something that takes a noticeable amount of time on iOS.
- Show loading UI.
- Do work.
- Transition to post-work UI.
To make the experience better (so that it doesn’t lock up), I pushed 2 onto the background thread, and then had to push 3 back on to the main thread – this is one of The Rules of iOS, all UI stuff has to happen on the main thread.
And then my tests started failing. Aha! A learning opportunity! (Also known as, super annoying as I had other things to get done that day).
Kind friends directed me to the XCTest feature of writing tests with asynchronous operations, which seemed a little confusing at first but is essentially:
- Create a thing (an “Expectation”).
- Include a timeout in your test that will allow it time to happen.
- Call “fulfil” on that expectation when your thing has happened.
Create The Thing
In your test class:
XCTestExpectation *expectation = [self expectationWithDescription:@"desc"];
Add The Timeout
After you call the function under test (before asserts, verifying mocks etc):
[self waitForExpectationsWithTimeout:1 handler:nil];
You might want to include a handler in the block, this wasn’t necessary for my purposes.
Calling Fulfil
This code is easy! It’s just:
[expectation fulfill];
The question was, where to put it? The examples seemed to put it in a completion block, but if you look back at my outline above, the end of my test was when the UI changes to the post-activity UI. Specifically, when a new ViewController is pushed on the stack.
So, I used a feature of the mocking framework (OCMock), that I can stub a method and then do something when it is called. So here, I stub the pushViewController method, because it’s the last thing that should get called, and when it is I set my expectation to be fulfilled.
OCMStub([mockNavController pushViewController:[OCMArg any] animated:YES]) .andDo(^(NSInvocation *invocation){ [expectation fulfill]; });
Voila!
For more detail on this and other aspects of iOS unit-testing (including unit-testing UI code!) you might find my digital workshop helpful.
5 replies on “iOS Testing: Handling Asynchronous Code”
iOS Testing: Handling Asynchronous Code http://t.co/lUJ3x0MJ0D
RT @catehstn: iOS Testing: Handling Asynchronous Code http://t.co/lUJ3x0MJ0D
Aha! I should try that, I have been rolling my own code to do something fairly similar.
[WORDPRESS HASHCASH] The poster sent us ‘0 which is not a hashcash value.
🙂 lmk how you get on!
[WORDPRESS HASHCASH] The poster sent us ‘0 which is not a hashcash value.
[…] iOS Testing: Handling Asynchronous Code […]
[WORDPRESS HASHCASH] The comment’s server IP (69.163.242.185) doesn’t match the comment’s URL host IP (69.163.242.203) and so is spam.