Tutorial: Automated Testing on iOS (with Appium, Test NG and Java on Mac)
Following up from our previous post on automated testing on Android — Here’s the next post in our series!
Part 1: Getting your environment setup with Appium and TestNG
Here’s a comprehensive list of everything you need on a fresh installation of OSX.
- Homebrew
- Carthage
- Node & NPM
- JDK
- Xcode
- authorize-ios
- ios-deploy
- ideviceinstaller
- ios_webkit_debug_proxy
- Appium
- Appium Doctor
- Maven
- Eclipse
- TestNg
Install Homebrew
Homebrew is a package management software that will make it much simpler for us to install a few other software.
To install, follow the instructions on this page: https://brew.sh/
This step will also install the Xcode Command Line Tools as part of the process.
Install Carthage
Carthage is a dependency manager. In our case, it is required by WebDriverAgent.
In terminal, enter the following:
brew install carthage
Install Node & NPM
Node is a javascript run-time environment and npm is the node package manager. We need these because Appium is a node application.
In terminal, enter the following: (this command will install npm as well)
brew install node
Install JDK and set JAVA_HOME
As we’ll be writing our tests in Java, we need the Java Development Kit (JDK)
Download the JDK, jdk-8u181-macosx-x64.dmg, from this link: http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html.
Then double click the JDK and follow through all the installation steps.
Next, you’ll need to set JAVA_HOME in your .bash_profile. To do so, first find the location of where the JDK was installed by entering the following into terminal:
/usr/libexec/java_home --v
The terminal output should be along the lines of
/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home
Next edit your bash_profile by editing it in vim editor. Type the following into terminal:
vim ~/.bash_profile
Press the ‘i’ key to enter insert mode then move the cursor to a new line and add the following 2 lines
export JAVA_HOME=[path of your java home]
export PATH=$JAVA_HOME/bin:$PATH
Press the ‘esc’ key and then type ‘:wq’ and press enter to save and quit vim. Restart Terminal to pick up the new .bash_profile settings.
Install Xcode and Simulators
Launch the Mac AppStore and download/install Xcode.
Once installed, Launch Xcode and select Xcode > Preferences > Components to install the simulators that you might want to test against.
Install authorize-ios
authorize-ios is a little utility that pre-authorizes Instruments to run UIAutomation scripts against iOS devices. You need this utility to run tests on real devices
In terminal, enter the following:
npm install -g authorize-ios
Install ios-deploy
ios-deploy is a small utility to install and debug iPhone apps from the command line, without using Xcode.
In terminal, enter the following:
brew install ios-deploy
Install ideviceinstaller
ideviceinstaller is a tool to interact with the installation_proxy
of an iOS device allowing to install, upgrade, uninstall, archive, restore
and enumerate installed or archived apps. You also need this utility to run tests on real devices.
brew install ideviceinstaller
**If you are macOS High Sierra or Mojave you may encounter an error involving “invalid active developer path” in which case run the following command in terminal:
xcode-select --install
sudo xcode-select -r
Then try install ideviceinstaller one more time.
Install ios_webkit_debug_proxy
Appium uses this tool to access web views on real iOS devices. In terminal, run the following command
brew install ios-webkit-debug-proxy
Install Appium
Appium is an open source test automation framework for use with native, hybrid and mobile web apps. It drives iOS, Android and Windows mobile apps using the WebDriver protocol.
In terminal, enter the following:
npm install -g appium@1.7.2
Install Appium Doctor
Appium doctor is a mini software that checks all (well, almost all) of the preconditions for appium to run successfully.
In terminal, enter the following:
npm install -g appium-doctor
At this point you can run the appium doctor to get a sense of what else we’ll need to get straight before we really get started. To run it, enter the following into terminal:
appium-doctor
appium-doctor’s output should look something like this:
Install Maven
Maven is a tool used for building and managing Java-based projects.
In terminal, enter the following:
brew install maven
Check the maven version by entering this into the terminal:
mvn -version
Next edit your .bash_profile by adding this line to it (follow the steps using vim above)
export PATH=/usr/local/Cellar/maven/3.5.4/bin:$PATH
(Be sure to set the version number as per the version you installed)
Install Eclipse
Eclipse is the Integrated Development Environment (IDE) that we are going to use to write, compile and run our test scripts.
Download and install Eclipse IDE for Java EE Developers from https://www.eclipse.org/downloads/packages/
Install TestNG
TestNG is a testing framework that we’ll be using to manage our automated test suite.
Launch Eclipse, select “Help” > “Install New Software” from the menu bar.
Paste “http://beust.com/eclipse” into the “Work with:” field and press Enter. Eclipse will take a while to fetch the relevant package to display, select the checkbox next to TestNG and click “Next” to complete the dialogs. Eclipse will take a while to install the TestNG package and then prompt you to restart Eclipse.
Part 2: Writing your test cases
If you’ve completed all the steps in Part 1 of this tutorial, your environment should be ready to create and run some automated test scripts. This section will guide you through the following steps:
- Setting up your project
- Writing a basic setup script
- Running your basic tests on a real device
- Writing actual tests!
Setup Your Project
Launch Eclipse, select “File” > “New” > “Maven Project” and follow through the dialogs with the default settings.
You will be required to enter a Group Id and an Artifact Id. The Group ID will uniquely identity your project across all projects. The Artifact ID is the name of the jar (without version). We recommend reverse-domain-packages for the Group ID (e.g. com.2359media.automation) and the project name for the Artifact ID (e.g. sampleproject). The basic maven project should look like this.
Next, let’s add the required JARs to the project. Double click on the pom.xml file and edit the <dependencies>…</dependencies> block to look like this:
<dependencies>
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>5.0.4</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.14.3</version>
<scope>test</scope>
</dependency>
</dependencies>
This defines the dependencies for maven to download into this project. Right click the project, select “Maven > Update Project…” to trigger downloading the dependencies. Once this is done, you should see a lot more .jar files in the Maven Dependencies dropdown of the Eclipse Project Explorer.
Write A Basic Setup Script
Before we can write a test script to ensure that everything is setup as expected, we need a test app. Let’s create one in Xcode.
Launch Xcode and create a new project. Select the Tabbed App template and then give it any product name that you want.
Now in terminal, change your directory to your project directory and compile a simulator-compatible .app build with the following command
xcodebuild -sdk iphonesimulator[XX.X]
e.g. xcodebuild -sdk iphonesimulator11.4
This will compile a build in the /build/Release-iphonesimulator folder of your project. You’ll need the path to this build for your test script.
Return to Eclipse where we’ll be writing our test script. In the Eclipse file explorer, expand “src/test/java” and expand the package. Double click the file “AppTest.java” and delete all its contents except the first line. Paste the following code into the file under the first line.
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import io.appium.java_client.MobileElement;
import io.appium.java_client.TouchAction;
import io.appium.java_client.ios.IOSDriver;
import io.appium.java_client.ios.IOSElement;
import io.appium.java_client.remote.MobileCapabilityType;public class AppTest {
public static URL url;
public static DesiredCapabilities capabilities;
public static IOSDriver<IOSElement> driver;
//1
@BeforeSuite
public void setupAppium() throws MalformedURLException {
//2
final String URL_STRING = "http://127.0.0.1:4723/wd/hub";
url = new URL(URL_STRING);
//3
capabilities = new DesiredCapabilities();
capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "iPhone Simulator");
capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, "iOS");
capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "11.4");
capabilities.setCapability(MobileCapabilityType.APP, "[PATH_TO_YOUR_.APP_FILE_THAT_YOU_COMPILED]");
capabilities.setCapability(MobileCapabilityType.NO_RESET, true);
capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "XCUITest");
capabilities.setCapability("useNewWDA", false); //4
driver = new IOSDriver<IOSElement>(url, capabilities);
driver.manage().timeouts().implicitlyWait(2, TimeUnit.SECONDS);
driver.resetApp();
} //5
@AfterSuite
public void uninstallApp() throws InterruptedException {
driver.removeApp("com.bzytan.appiumeveryday");
} //6
@Test (enabled=true) public void myFirstTest() throws InterruptedException {
driver.resetApp();
}
Here’s a brief rundown on what this all means
- //1 — Functions that have the @BeforeSuite annotation will be run before every test suite run. Such a function can be used to setup the application into the right state/condition before the individual test cases are run.
- //2 — We set the URL to the localhost where we will be running Appium
- //3 — Setup an object that will hold the Appium Capabilities
- //4 — Initialise the driver with the URL and Capabilities object. This driver will be the main object used to interact with the device
- //5 — Functions that have the @AfterSuite annotation will be run after every test suite run. These functions should be used to clean up any changes done on the device before the next test suite run.
- //6 — Functions that start with @Test will be identified as TestNG tests. you can enable or disable a test in the test suite by toggling the value in (enabled=true). This test at the moment will just reset the application and no nothing else.
Don’t forget to edit this line of code!
capabilities.setCapability(MobileCapabilityType.APP, "[ABSOLUTE_PATH_TO_YOUR_.APP_FILE_THAT_YOU_COMPILED]");
Now start the appium server by entering the following into terminal
appium
If you’ve set everything up correctly, you should be able to right click on the myFirstTest method in AppTest.java, and select “Run As > 1 TestNG Test”. You should see the simulator launch, the app get installed and then launched a few times and your test case should complete successfully.
Setup To Run A Test On A Real Device
In order to run apps on real iPhone devices, there are quite a number of tedious things that you’ll need to do.
- Join the Apple Developer Program
- Compile an .ipa file that can be installed on your specific device
The how-to of the above steps are beyond the scope of this tutorial and we’ll continue assuming that you already have yourself registered and have an .ipa file that is built for installation on your device.
Locate your Apple Developer Team ID then add the following capabilities to your test script setup:
capabilities.setCapability("xcodeOrgId", "[YOUR_TEAM_ID]");
capabilities.setCapability("xcodeSigningId", "iPhone Developer");
Now connect your iPhone to your computer by USB and load up iTunes to get the UDID of your iPhone. Add another capability to your test script setup:
capabilities.setCapability(MobileCapabilityType.UDID, "[YOUR_DEVICE_UDID]");
Locate your .ipa file and modify the APP capability to:
capabilities.setCapability(MobileCapabilityType.APP, "[PATH_TO_YOUR_IPA_FILE]");
Also modify your DEVICE_NAME capability to the name of your device (On your iPhone. “Settings> General > About > Name”)
capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, “[YOUR_DEVICE_NAME]”);
Lastly —We need to create a provisioning profile for signing the WebDriverAgentRunner. Launch up Xcode and create a new project, select the team and enter the project name as follows:
Take a look at the “Project” tab of your project settings to confirm that “Automatically manage signing” is selected, and a Xcode Managed Profile is created.
Copy your bundleId and add the following capability to your Appium Capabilities setup
capabilities.setCapability("updatedWDABundleId", "[YOUR_BUNDLE_ID]");
Your capabilities setup should now look like this
capabilities = new DesiredCapabilities();
capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, "iOS");
capabilities.setCapability(MobileCapabilityType.NO_RESET, true);
capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "XCUITest");
capabilities.setCapability("useNewWDA", false);capabilities.setCapability("xcodeOrgId", "[YOUR_TEAM_ID]");
capabilities.setCapability("xcodeSigningId", "iPhone Developer");
capabilities.setCapability(MobileCapabilityType.UDID, "[YOUR_DEVICE_UDID]");
capabilities.setCapability(MobileCapabilityType.APP, "[PATH_TO_YOUR_IPA_FILE]");
capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "[YOUR_DEVICE_NAME]");
capabilities.setCapability("updatedWDABundleId", "[YOUR_BUNDLE_ID]");
** if you encounter an error along the lines of “Could not determine Xcode version” on trying to run your test case on High Sierra or Mojave. run the following command in terminal and try again:
sudo xcode-select -r
** if you encounter an error along the lines of “Unable to launch WebDriverAgent because of xcodebuild failure: “xcodebuild failed with code 65”" — more troubleshooting help can be found here: https://github.com/appium/appium-xcuitest-driver/blob/master/docs/real-device-config.md
W̶r̶i̶t̶i̶n̶g̶ ̶A̶n̶ ̶A̶c̶t̶u̶a̶l̶ ̶T̶e̶s̶t̶
Since we can’t create a common iOS .ipa file for our readers to reference and follow along writing test cases on. We recommend reading this same section on our Android post to have a better idea on the general structure of a test case. The key difference is simply how you go about identifying the elements to interact with.
Identifying iOS Elements
While Android provides the uiautomatorviewer tool, Apple doesn’t provide anything out of the box. Instead download and install the Appium Desktop application and use the Appium Desktop Inspector functionality.
(Note that Appium desktop also contains the Appium server that we previously installed with terminal — but it behaves slightly differently so we won’t be using that only for inspection purposes and not to run our test suite.)
Launch the Appium Desktop app, select “Simple” and then click the “Start Server” button
Select the search icon to Start Inspector Session
Under Automatic Server, enter the following desired capabilities as per your test script.
- deviceName
- udid
- platformName
- platformVersion
- app
- noReset
- updatedWDABundleId
Click Start session and the inspector will present itself and launch the application on the device. You can now easily navigate the application and with the “Tap”, “Send Keys” or “Clear” action buttons on the inspector and also view either the element’s ID or xpath to uniquely identify the element to interact with!
That’s all we’ve got for now on the basics of automating iOS testing. We’re well aware of how painful the setup can be so reach out to us if you’re having trouble and we’ll try to help!
Stay tuned for more on automated testing ;)