Scalable UI Tests for iOS Applications

Russell Yeo
Engineering at Depop
5 min readFeb 6, 2020

--

Person holding iPhone

Modern mobile applications evolve quickly. As we roll out new features we need to check that existing features still work as expected to ensure that our users have a good experience. In practice this often means a QA team manually testing the whole app for regressions; however, this can be time and resource intensive. One way to ease this dependency is to start writing automated UI Tests to supplement manual testing. Doing so will free up your QA team to focus on testing new features and engage in exploratory testing to find edge cases that aren’t covered by your test suite. Developers will be able to write new features safe in the knowledge that the existing functionality is covered by the UI tests and the business will be able to move faster in building the product and releasing new features.

What Is UI Testing?

Automated UI testing is a testing approach for the purpose of increasing confidence in the behaviour of your application. Executing a UI test usually means running a simulated environment and performing an automated set of interactions, then asserting that the actions have produced an expected change on the screen. UI Tests are a great tool for verifying that your most important flows continue to work as expected as you add new features to your application.

Note: Snapshot tests are great for testing layout and can be used in conjunction with UI Tests verifying the behaviour.

Xcode already comes with a built in testing framework — XCTest, which includes everything you need to perform UI tests. However knowing how to structure your UI tests can be a challenge.

An example

To demonstrate, we’ve used the code from the Handling User Input — SwiftUI tutorial from Apple where you can view a list of landmarks and add them to your favourites.

The flow we are going to test looks like this:

  1. Find a landmark in the list.
  2. Select it to view the detail screen.
  3. Press the favourite button.
  4. Go back to the list screen.
  5. Verify that the landmark is now in your favourites.

Here is what the UI test might look like for this flow:

Looks straightforward, however it is hard to read and much of the code is not reusable. This is fine for a simple test like this, but will quickly become painful once you start testing more complex features.

UI Testing Microframework

To facilitate simple UI testing we developed a microframework based on the Page Object model. This powerful yet simple pattern enables us to add a layer of abstraction over the application code—the PageObject, or in our case ScreenModel. We can then use this abstraction to interact with the underlying UI through a focused API and avoid any problems that might arise from calling the code directly.

Notice how the following lines in this test case read almost exactly like the requirements we specified above:

As you can see it has a simple and easy to read declarative style. after instantiating the LandmarkListScreen (this isn’t the actual UI class, more about that later), each line that follows describes either an action to be performed or an assertion to verify changes have been made on the screen.

How It Works

In the UITestCase class, which test classes subclass, we can manage the life cycle of the test application and inject environment variables at launch. Configuring the app with environment variables is a useful way to set up the app in a state that is suitable for UI testing (e.g. connected to a mocked server).

The UITestContext property allows us to pass the current test case and the application with its current state between screens via the ScreenModel.

Each ScreenModel subclass contains:

  • XCUIElement computed properties to represent the views on the screen.
  • Functions to interact with the elements on the screen.
  • XCTest assertions to verify expectations of what should appear on the screen.
ScreenModel for LandmarkList Screen
ScreenModel for LandmarkDetail Screen

To expound on the above a little further:

The landmarkRow function queries the application view hierarchy for a button inside a TableViewCell with a name passed as an argument.

The favoriteButton computed property queries for a button with an accessibility identifier: landmark-detail.button.favorite. For this to successfully find the button in question you’ll need to set the accessibility identifier on the underlying view — this is the best way to uniquely identify an instance of a view.

The selectLandmark function allows us to interact with a row in the table through the XCUIElement properties. These functions always return a ScreenModel, either the current one or the next screen we will present after performing an action. This allows us to chain actions in the UI test, passing the context object through the screens and gives us that lovely dot syntax in the test case.

Conclusion

While it is still a work in progress, this solution has been largely successful for us. Some of our key takeaways include:

  • The interface is kept as simple as possible, allowing the whole team to get started on writing UI tests - including QA employees with little coding experience.
  • The framework shares lots of code between tests, reducing the amount of overhead in adding UI tests to a new feature — we want contributing to the UI tests to be as frictionless as possible.
  • UI Tests can take a long time to execute! We have partially solved this issue by parallelising our continuous integration builds — one job to build the application and another to run the test suite.
  • We are still working on a painless solution for pairing this framework with a backend API mocking library (We are currently using WireMock to mock responses along with Dredd to validate API contract with our backend), some challenges with this include; uncomplicated stateful behaviour and sharing code with our Android counterpart app.

You can find the source code and the demo project from this post here.

We’re hiring!

You can check out our open roles here and follow our LinkedIn page. If you don’t see the perfect role for you, or just want to pop by our offices for a chat and a tour get in touch: work@depop.com

--

--