UI Tests on iOS
By: Paweł Zemsta
Tags: Mobile App Development, Testing, QA, Test Automation, iOS App Development

UI Tests on iOS

Testing automation done right

Many of today’s mobile applications display customized content to user, base on dynamic parameters. The Azimo application is no different.

With our mobile platform, our customers can transfer money to almost every country all over the world. There are literally thousands of combinations of sending and receiving countries, payments, and delivery methods.

Combinations of these parameters can be a big challenge for the QA engineering team. Ideally, every possible case should be covered. That’s why It’s not the best idea to test this type of functionality manually. We need to write test automation not to do this tedious work over and over again.

Read more about why “Automated testing will set your engineering team free.”

In this article, I will show how we approach writing automation tests for functionalities with dynamic content while trying to create the most flexible and readable code.

1. What are we going to test?

The Azimo app helps our customers to transfer money around the world. To ensure that the user experience is at the highest possible level, we introduced a functionality called “How it works.” It explains what to do to send money to the customer’s recipient, with the selected delivery method. Let’s prepare the testing automation for it together.

Here are the structure of the functionality and the expected test result. From a technical point of view, this feature is a bottom sheet containing a four-step explanation of how to create a transfer and additional information about the selected destination country.

It looks like this:

Navigation bar:

name of the country to which we want to send money,

short description

Content:

header,

four steps to send a transfer, each including:

- title,

- description

Now, I want to implement tests that are comparing data of two objects. It’s a standard assertion, where the first object contains expected data, and the second is fetched from the application (so this is what users see on the screen).

This is how the assertion looks like:

AssertEqual(ObjectWIthExpectedData, ObjectWithFetchedData)

First thing — we have to consider whether each of the above elements will be present for every single test case. In our case, the answer is “yes”. It means that for each country + delivery method combination, we display the name of the country, short description, header, and four steps.

If this isn’t true (e.g., some configurations have fewer or more steps), we would need to set some parameters as optional. In that situation, we would need to find the boundary conditions (a minimum and a maximum number of steps) and set everything except the minimum/maximum number of steps as optional.

2. Navigate to the bottom sheet

Let’s start by navigating to the How it works bottom sheet in the app. In the verifyHowItWorksBottomSheet() function, wait until the How it works button appears and click on it to start verifying the correctness of the displayed data (the description of the entire function is presented later in this article).

3. Create an object that collects data visible on the screen

As I mentioned in the previous point, each step in the instruction contains a title and a short description. So we create a new HowItWorksBottomSheetCell — the class responsible for getting this data from the screen. To simplify and unambiguous searching for elements in the DOM tree, I added appropriate identifiers for them. The title is marked with the HowItWorksSheet_title identifier, while the description is HowItWorksSheet_subtitle.

4. Create an object containing the expected data

Now let’s move on to creating an object with data that we expect to be displayed in the application for a specific country + delivery method combination.

We create two structures HowItWorksBottomSheetData and Cell with parameters:

title

navbar Title

navBarSubtitle

position1, position2, position3, position4, which will be of the Cell type and contain parameters:

- title

- subtitle

Because the title, the description in the navigation bar, and the title in the content section will be the same for each step, we provide them only once for the entire object. However, the content (title, description) of individual steps will be different.

As for different combinations, we expect different output, using the parameters passed in the function for(country, deliveryMethod) and the conditional statement switch, I will be able to choose the option we are interested in and extract the necessary data from it.

The for function takes two parameters, which are enums:

country type: CountryLabel

type: deliveryMethod type: DeliveryMethod

For example, for country Philippines and delivery methods Cash pick-up we will select accordingly:

case(.philippines, .cashPickUp).

Remember to cover all the most critical test cases. If some of them overlap, i.e., we expect the same data, we can group them.

5. Create a function comparing the data of both objects

Now, when we have an object with expected data and the one with data taken from the app, we need to compare them. For this purpose, we will write the function verifyHowItWorksBottomSheet(), which takes two parameters: country and deliveryMethod.

As you can see in the above screenshot, we also use the howItWorksBottomSheetGetCellAtIndex() function, which returns a HowItWorksSheetCell (step) object in the appropriate position (index). The implementation is:

We retrieved all expected data for a specific country + delivery method to the data variable.

In case you cannot download data to the data object, the test will fail with appropriate information about the error in the logs.

The closeButton, navBarTitle, navBarSubtitle, and header variables only appear once on the screen, so there is no need to create a separate class for them as they will not be reused. We can declare them as variables in the base class and compare the contents like the rest to the data object’s parameters.

This is how we call the comparison function in the test:

To make the logs in the console and test logs in Xcode more transparent, I use XCTActivity, making it clear and legible for me to see when the bottom sheet was verified:

6. Summary

Here are the advantages of the presented solution:

In the case of changes to the application code or translations, we have to change only one place in the tests, not in each test case separately.

We have clear and transparent code even for non-technical people.

We cover all expected test scenarios.

The solution is easy to maintain and extend.

We have great control over data from the test level.

I hope the solution I have presented will allow you to look at writing automated tests from a different perspective. I highly encourage you to implement it in your project and let me know how it goes. If you have any questions or suggestions, feel free to leave a comment or contact us directly.