Asynchronous UI Testing with Xcode
When UI testing with Xcode 7’s new XCTest framework, you may find yourself in need a of a reliable way to make asynchronous calls when testing various components. I’ve created a simple application for you to use to see this concept in action.
Before you scroll below or download the app, first I will give you a big picture of what the scenario is. I have an application that currently displays a submit button. Upon pressing the submit button, a request that takes between 1 and 5 seconds occurs. After the request is complete, a contact button becomes available.
What I’d like to do is simulate a UI test by pressing the submit button and then press the contact button. The only issue is that the submit button can take any time between 1 and 5 seconds! So what should I do?
Locating The Problem
First download the app here.
Once the app is downloaded, navigate to “SubmitViewController.m”
You’ll find the following method in that file:
This method simulates some call to a server that takes an indeterminate amount of time. This is what makes UI testing slightly more complicated as we now have to account for async calls.
Take some time to look over the code. This tutorial assumes you know the basics of iOS development.
Run the app with
CMD-R to get an idea how this basic app works.
Now that you have an idea as to how this works, let’s move on to some basic UI testing.
The Wrong Approach
Navigate to “Async_UI_TestingUITests.m”
You’ll find 3 tests. - testButton - testButtonSync - testButtonAsync
Go ahead and run the tests with
CMD-B + CMD-U
Now run the tests a few times. What did you notice?
2 of the 3 tests should pass consistently but one of the tests will fail. Why is that?
The issue is in the testButton method.
This method assumes that the submit button has no delay but in reality it may have a delay between 1-5 seconds!
The Naive Approach
Now take a look at the testButtonSync method:
Notice that the tester decided to fix the issue of not knowing the response time by delaying the app by 10 seconds. This works but now you’ll have to wait for 10 seconds every time you run the test simulator!
We want a way to click the button as soon as it’s available.
The Smart Approach
Now take a look at the testButtonAsync method:
In this example, I am using a predicate to check when
contactButton is available:
[self expectationForPredicate:exists evaluatedWithObject:contactButton handler:nil];
I then wait at most 10 seconds for the contact button to appear:
[self waitForExpectationsWithTimeout:10 handler:nil];
Once the button appears the contact button will be tapped immediately!