How To Solve Problems With UI Testing

Mocking APIs and maintaining tests with the Page Object Model

Importance of UI Testing

Unit tests are important. They make sure our functions work properly, that we didn’t break anything, and to catch edge cases.

From the user’s perspective, however, it doesn’t matter if pressing the confirm button doesn’t lead them to the expected screen. So, UI testing doesn’t replace unit testing in your test suite, it just completes it.

Problems of UI Testing

I don’t know how many developers maintain unit testing over time, but I know that UI testing is still pretty rare among developers. For good reasons, actually.

UI testing can be hard to set up to keep from breaking up over time, due to the following reasons:

  • Our app works with a back-end server, so tests become very inconsistent: network issues or server issues. We don’t have control over the requests returning from the server.
  • Writing UI test scripts takes a lot of time. Locating buttons or handling navigation between screens for a single test. And then we have to duplicate the code when we want to create a new test case.
  • Not only are the tests long to write, they are also not very readable. Try to understand them two days after their creation and you’ll know what I mean.
  • UI is changing all the time. We add and remove buttons, screens, scrolls, and more. It becomes a nightmare updating tests every time we want to make a small change.

Simulate Your Back End

Most of the mobile apps work with a back-end server and for UI testing this can be a challenge.

The first rule of any automatic testing is that, if you run your tests 1000 times, you should expect the same results each time. So, server and network issues make your tests unreliable.

But, if you follow SoC (separation of concerns), you can simulate your back-end server by creating a mock server, implementing it in your network layer and making your test fully reliable and predictable.

Many tools can help you mock your server but the principles are always the same — replace your server with a local component that returns predictable and reliable response objects to your network requests.

To test your app in various cases (log-in, sign-out, data sync, etc.), you can save your “request->response” mock data to different JSON files where each file represent different scenarios (log-in, register).

You load your test with the preferred file using the launch options, so you’re not only solving the reliability issues, but you can also test your app with different cases.

import UIKit
protocol RestServerMockProtocol {
// get return a server response based on url and paramteres
func getResponseFor(urlString : String, with parameters : [AnyHashable : Any])->[AnyHashable : Any]
// load responses by filename. Needs to be done every time you start a test
func loadResponses(fromFile filename : String)
// insert a new request to the data. At the end save the result to the db
func recordRequest(request : String, with parameters : [AnyHashable : Any], response : [String : Any])

But it takes time to build all the mocking data!

You are a developer, do your magic! “Record” requests and responses while running your app once. You can do that with Charles Proxy or write some code that saves all the network data to a file.

Mocking Other APIs

This method doesn’t have to end with only mocking your server. You can mock other APIs as well.

For example, let’s say you have an app that manages address book contacts and it syncs with the device’s address book.

Again, if you separated your address book connector from the rest of the app and followed protocol-oriented design patterns, you can replace your connector with a mock address book and return different contact’s objects, no matter what address book the device currently holds.

The same goes for calendars, reminders, and other system APIs.

Maintaining Tests With Page Object Model

Writing UI test scripts takes time and effort but the real problem is how to maintain it over time.

The simplest way to solve this problem is to create something called a Page Object Model.

A Page Object Model is an object attached to a screen. It has a reference to XCUIApplication and its responsibilities are to take actions on the page, locate elements and return the next page object if needed.

Note that I’m not planning on showing you how to write UI testing scripts but rather, I’d like to show you how to solve day-to-day problems that occur when setting up a good testing infrastructure for your app.

import XCTest
class SignUpScreen: NSObject {
let app = XCUIApplication()
func doSignIn(email : String, password : String)->OnboardingPageObject {
// fill the email text field
// fill the password text field
// press the go button
// return the next page object which is the onboarding page
func isCurrentPageIsSignup()->Bool {
// check if this is the signup page for validation
private func getEmailTextField()->XCUIElement {
// return the XCUIElement of the email text field
private func getPasswordTextField()->XCUIElement {
// return the XCUIElement of the email text field
private func getGoButton()->XCUIElement {
// return the XCUIElement of the go button
privaet func pressGo() {
// press the go button
import XCTest
class MainScreenTests: XCTestCase {
override func setUp() {
continueAfterFailure = false
let app = XCUIApplication()
app.launchArguments.append("-mockServer") // launch the app with mock server argument
func testAddingTask() {
let startUpScreen = AppWelcomeScreen() // this is the start up page
let signupPage = startUpScreen.pressTheLoginButton() // pressing the login button on the startup page, leads us to the sign up page
let onboarding = signupPage.doSignIn(email: "", password: "123456") // we do sign in with email passowrd
let mainScreen = onboarding.pressSkipButton() //tapping the skip button on the onboarding screen
mainScreen.validateScreen() // verify we are on the main screen
// or event the short version!
func testAddingTaskShort() {
let mainScreen = AppWelcomeScreen().pressTheLoginButton().doSignIn(email: "", password: "123456")

The principles of Page Objects is that we separate the test script from handling the layout.

This lets us quickly write very small, readable UI tests and, at the same time, reuse the hard work by building a dedicated object for each screen.

Also, when your UI changes, all you have to do is modify the relevant methods in your Page Object, without touching your actual test cases.


This was a short introduction on how to overcome issues with UI testing.

As I said, there are additional and more sophisticated tools, such as setting up your local server for mocking, Screenplay Pattern, or Xcode’s configuration files. I’ll try to get into those in future articles.

The most important thing is that you need to set up a stable, readable, and reusable UI testing infrastructure and stop wasting precious time by letting Xcode do the sanity checks for you.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s