Imagine being able to write scripts that automatically interact with your iOS application and be able to verify the results. With UI Automation you can. UI Automation is a tool provided by Apple to carry out high-level testing on your iOS application beyond anything that can be achieved with XCTest.
White Box versus Black Box Testing
You may have heard the comparison of white box testing versus black box testing with regard to how someone can test a piece of software. If you are not familiar with these concepts, let me explain how they work.
White Box Testing
Imagine there is software running in a box. With white box testing, you can look inside the box and see all the rough cuts about how the software works, and then make educational decisions about how to test the software. You can also have a deeper hook level to the software from the test you wrote.
Unit testing is white box testing. When writing unit tests, testers have smooth access to the code that is being tested. Testers can actually write tests that utilize software that is tested on a method, or unit, level.
In developing iOS software we use the XCTest framework to do this type of testing. Please see the other tutorials that I wrote about getting started with XCTest.
Black Box Testing
In testing the black box, the box is opaque. Testers can’t see inside the box. Testers cannot access and do not know about implementing a code base for writing tests. Instead, testers are forced to use the application as an end user by interacting with the application and waiting for their response, verifying the results.
There are at least two ways to execute this type of test.
Testers repeatedly and manually take a number of steps that have been determined and visually verify the results.
Use special tools to test applications with APIs that behave similar to how humans interact.
In developing iOS applications, Apple provides a tool called UI Automation to do black box testing.
What is UI Automation?
UI Automation is a tool that Apple provides and maintains for testing top-level, automated, and higher iOS applications. The test is written in JavaScript, following an API defined by Apple.
Writing tests can be made easier by relying on accessibility labels for user interface elements in your application. But don’t worry, if you don’t have it, there are alternatives available.
The UI Automation API lacks the typical xUnit based format for writing tests. One difference with unit testing is that the tester needs to manually log successes and failures. The UI Automation Test is run from the Automation framework in the framework tool which is equipped with Apple’s developer tools. The test can be run on an iOS Simulator or on a physical device.
Writing UI Automation Tests
Step 1: Open the Sample Project
I have updated the sample project used in the previous tutorial on iOS testing with some additional user interface elements that provide some useful hooks for adding UI Automation testing. Download the project from GitHub. Open the project and run the application to make sure everything works as expected. You should see a user interface similar to the one shown below.
Before we write any tests, don’t hesitate to try the sample application to get used to its functions. As a user, you can enter text in the text field and tap the button to see a label on the screen that displays the returned and entered string.
Step 2: Create a UI Automation Test
Now that you are familiar with the sample application, it’s time to add the UI Automation test. UI automation is a tool that can be found in Instruments. To run the sample application in Instruments, select Products> Profiles from the Xcode menu. Select Automation from the list of tools.
The main Instrument window will open with a single instrument ready to run, the Automation instrument (the Automation instrument runs the UI Automation test case). You will also see an area at the bottom of the window that looks like a text editor. This is a script editor. This is where you will write your UI Automation test. For this first test, follow the instructions below, adding each line to the script in the script editor.
Start by saving a reference to the text field in a variable.
var inputField = target.frontMostApp().mainWindow().textFields()[“Input Field”];
Set the value of the text field.
inputField.setValue(“hi”);
Verify that the score has been set and, if it has, passed the test. Fail the test if it doesn’t.
if (inputField.value() != “hi”) UIALogger.logFail(“The Input Field was NOT able to be set with the string!”);
else UIALogger.logPass(“The Input Field was able to be set with the string!”);
While this test is quite trivial, it does have value. We just wrote a test that tests the existence of text fields when the application is launched and tests whether random strings can be specified as text field values. If you don’t trust me, delete the text field from the storyboard and run the test. You will see that it failed.
This test shows three important parts of writing a UI Automation test. First, it shows you how to access simple user interface elements, text fields. Specifically, we access the dictionary of all text fields in the application’s basic view via target.frontMostApp (). MainWindow (). TextFields () and we then find the text field that we are interested in by searching for one with the Input Field key. This key is actually a text field accessibility label. In this case, it is defined on the storyboard. We can also set accessibility labels in the code using the accessibilityLabel property in NSObject.
Access the application’s main window, the most advanced application, and general targets when working with UI Automation. I will show you how to make this easier and less verbose later in this tutorial.
Second, it shows you that you can interact with the user interface elements on the screen. In this case, we set the value of the text field, imitating users who interact with the application by entering text into the text field.
And third, this example also shows techniques for verifying what is happening in the application. If the value is successfully set, the test is skipped. If the value is not set, the test failed.
Step 3: Save the Test
When writing tests in a practical script editor, it quickly becomes complicated and difficult to manage. If you exit the Instrument, all unsaved changes will be discarded. We need to save the test we wrote. Simply copy and paste your test into a new document in your favorite text editor and save it. You can find tests made in this tutorial in the sample project under Jumblify / JumblifyTests / AutomationTests.js.
To run the test, select the middle tab in the right panel, next to the script editor, and select Add> Import.
You will be asked to choose a script to import. Navigate to the saved and import script. You can still change scripts in the script editor. Any changes will be automatically saved in the external file that you made.
Step 4: Tap the Button
Let’s update our tests to test interactions with buttons. Our test has added text to the text field so we only need to add code to tap the button. First let’s think about how to find the button on the display so that it can be tapped. There are at least three ways to achieve this and each approach has its sacrifice.
Approach 1
We can programmatically tap coordinates (X, Y) on the screen. We do this with the following line of code:
target.tap ({x: 8.00, y: 50.00});
Of course, I don’t know if that even coordinates the buttons on the screen and I won’t worry about that, because this approach is not the right tool for this job. I just mentioned it so you know it exists. Using the tap method on a target to tap a button is error-prone, because the button may not always be at a certain coordinate.
Approach 2
You can also find the button by searching for the key arrangement in the main window, similar to how we accessed the text field in the first test. Instead of directly accessing keys using a key, we can take a series of buttons on the main window and hard array index code to get a reference to the button.
target.frontMostApp (). mainWindow (). buttons () [0]. tap ();
This approach is a little better. We don’t encode coordinates, but we generate array codes to find the button. If we add another button on the page, it might accidentally damage this test.
Approach 3
This brings me to the third way to find buttons on a page, using accessibility labels. By using the accessibility label, we can directly access the button just like we will find the object in the dictionary using the key.
target.frontMostApp (). mainWindow (). buttons () [“Jumblify Button”]. tap ();
However, if you add the above line to the script and run it, you will get an error.
This is because we haven’t specified an accessibility label for the button. To do that, open Xcode and open the project storyboard. Find the button on the display and open the Identity Inspector on the right (View> Utilities> Identity Inspector). Make sure that Accessibility is turned on and set the Label for the button to the Jumblify Button.
To run the test again, you must run the application from Xcode by selecting Product> Run and then profile the application again by selecting Product> Profile. It runs tests and every test must pass now.
Step 5: Check the Mixed String
As I mentioned before, our application takes a text string as input, and when the user taps the button, it displays the reverse string. We need to add one more test to verify that the input string is reversed correctly. To verify that the UILabel is filled in with the correct string, we need to find out how to reference the UILabel and verify the string that it displays. This is a common problem when writing automation tests, which is figuring out how to reference elements in an application to make statements on it.
There are methods in almost every object in the UI Automation API, logElementTree. This method records the nested elements of the given element. This is very useful for understanding the hierarchy of elements in an application and helps to find out how to target certain elements.
Let’s see how this works by cutting down the element tree from the main window. Look at the following line of code.
target.frontMostApp (). mainWindow (). logElementTree ();
Add this line to the results of the test script in the following output:
As you can see, there is a UIAStaticText sub-element of UIAWindow and you can also see that it has the name ih, which is also an inverted string that we need to verify. Now, to complete our test, we only need to add code to access this element and verify that it exists.
Why do we only need to verify if the UIAStaticText element is present? Because the name of the element is an inverse input string, verifying its presence confirms that the string is reversed correctly. If the element does not exist when referenced by the name – the reverse string – then that means the string is not reversed correctly.
var stringResult = target.frontMostApp().mainWindow().staticTexts()[“ih”];
if (! stringResult.isValid()) UIALogger.logFail(“The output text was NOT set with the correctly reversed string!”);
else UIALogger.logPass(“The output text was set with the correctly reversed string!”);
Scratching the Surface
There are many other ways that end users can interact with iOS devices when using your application. This means there are many other ways that you can use UI Automation to simulate this interaction. Instead of trying to capture a complete list of these interactions, I will direct you to the UI Automation reference documentation.
For each type of object that you can use to interact, you can see a list of methods available on that object. Some methods are to take attributes about objects while others are to simulate touch interactions, such as flickInsideWithOptions in UIAWindow.
As you try to test more and more complicated applications with UI Automation, you will find that it is sometimes quite tedious to repeatedly use logElementTree to find the element you are looking for. It also becomes boring and complicated for applications with a complex display hierarchy or navigation. In this case, you can use the Other Instruments feature to record a series of user interactions. Even cooler is the Instrument generating JavaScript Automation UI code needed to reproduce recorded interactions. Here’s how you can try it for yourself.
In Instruments and with the Automation instrument selected, look for the record button at the bottom of the window.
If you click the record button, the Instrument will start the recording session as shown in the image below.
Instruments Screenshot showing capture in progress
The instrument will launch your application in the iOS Simulator and you will be able to interact with it. The instrument will generate scripts based on your interactions in real time. Try. Play the iOS Simulator, tap on a random location, do swipe gestures, etc. This is a very useful way to help explore the possibilities of UI Automation.
Avoid Monolithic Code Bases
As you might be able to predict, if we keep adding more tests to the test file that we made using the same method, it will quickly become difficult to maintain. What can we do to prevent that from happening. In my test, I did two things to solve this problem:
One test for one function: This implies that the test we write needs to be focused on a particular functional part. I will even give it a proper name, like testEmptyInputField.
Group related tests in one file: I also group related tests in the same file. This makes the code in one file manageable. It also makes it easier to test separate functions by running tests in certain files. Additionally, you can create master scripts where you call functions or tests that you have grouped in other test files.
In the following code snippet, we import a JavaScript file and this makes the function in the JavaScript file available to us.
import “OtherTests.js”
Conclusion
In this tutorial, you have learned the value of higher level testing and how UI Automation can help fill that gap. This is another tool in your toolbox to help ensure you send reliable and robust applications.
Reference
JavaScript Reference Automation UI
Source Article: https://code.tutsplus.com/tutorials/introduction-to-ios-testing-with-ui-automation–cms-22730?ec_unit=translation-info-language