Appium 2.0 iPhone Web Test using JUnit 5

February 6, 2023
 
Creating an automated test to launch and interact with a Safari web browser on an iPhone
This article is part of a series which provides sample Java code to run a JUnit 5 tests that will interact with mobile devices and simulators using Appium 2.0 to perform automated testing:
  • Install Appium
  • iOS automated web test (this article)
  • iOS automated local native application test
  • Android automated web test
  • Android automated local native application test

Appium

The first step is to ensure that Appium is running. If you need assistance, you can refer to this article. Appium will provide the means to communicate with your iPhone. In this example I will be accessing an iPhone 14 Pro running iOS version 16.

Errors

Appium requires a significant amount of configurations and dependencies. If you are encountering errors during the implementation of this code, I highly recommend installing and running appium-doctor. You could find details in the installation article referred to in the series.


Create Project in Eclipse

You can build your project in your IDE of choice, in this example we will be using Eclipse.
Launch Eclipse and create a new gradle project by clicking on File | New | Other... | Gradle | Gradle Project

This will create an empty project with a sample class and test in the src/main/java and src/test/java respectively. It will also include the gradle configurations.

Gradle

We will be using gradle to install our dependencies. Please update your build.gradle to match the xml below. The only section that should be different should be the dependencies.
/*
 * User Manual available at https://docs.gradle.org/7.4.2/userguide/building_java_projects.html
 * Source: https://www.code-gorilla.com/
 */

plugins {
    // Apply the java-library plugin for API and implementation separation.
    id 'java-library'
}

repositories {
    // Use Maven Central for resolving dependencies.
    mavenCentral()
}

def jUnitVersion = '5.8.1'
def appiumVersion = '8.3.0'
dependencies {
    // Use JUnit Jupiter for testing.
    testImplementation "org.junit.jupiter:junit-jupiter:${jUnitVersion}"
    testImplementation "io.appium:java-client:${appiumVersion}"

    //Start: Binding slf4j to an implementation
    testImplementation 'org.slf4j:slf4j-jdk14:2.0.6'

    // This dependency is exported to consumers, that is to say found on their compile classpath.
    api 'org.apache.commons:commons-math3:3.6.1'

    // This dependency is used internally, and not exposed to consumers on their own compile classpath.
    implementation 'com.google.guava:guava:30.1.1-jre'
}

tasks.named('test') {
    // Use JUnit Platform for unit tests.
    useJUnitPlatform()
}
Gradle Refresh
After you have updated the build.gradle file you will want to synchronize gradle to install the dependencies.
You can do this by right clicking anywhere in the editor window | right click | Gradle | Refresh Gradle Project

Xcrun

Please run the following command at the terminal to get your UDID and device name:
Xcrun simctl list | grep Booted

Create New Test Package

Before creating the class I would recommend creating a new package within the src/test/java folder to put your new class into. This is an optional step, but a recommended one. When you create the new package, it is recommended to name it with your domain name backwards. For example, com.code_gorilla.appium is what I will be using in this demonstration.
You can do this by right clicking on src/test/java | New | Package

Create New Test Class

If you created a new package, create a new class within that package; otherwise put it anywhere within src/test/java.
You can do this by right clicking on your package | New | Class

I will be using iOSWebTest as follows:
package com.code_gorilla.appium;
package com.code_gorilla.appium;

import java.net.URL;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;

import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;

import io.appium.java_client.ios.IOSDriver;
import io.appium.java_client.remote.AutomationName;
import io.appium.java_client.remote.MobileCapabilityType;

/**
 * Appium 2.0 JUnit 5 automated test launching Safari on iPhone
 * @author Tom Snyder
 * 
 * References:
 * https://www.code-gorilla.com/view-article/appium-2.0-iphone-web-test-using-junit-5
 * https://appium.github.io/appium/docs/en/2.0/intro/
 * https://junit.org/junit5/
 */
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class iOSWebTest {
	private static IOSDriver driver = null;
	private static int APPIUM_VERSION = 2; // ONLY 1 and 2 are valid options for major releases.

	@BeforeAll
	public static void beforeAll() throws Exception {
		System.out.println("Initializing Appium Connection...");

		DesiredCapabilities capabilities = new DesiredCapabilities();
		capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "iPhone 14 Pro");
		capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "16.2");

		capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, "iOS");
		capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, "Safari");
		// capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, "Chrome");
		// BROWSER NAME or APP; not both.
		// capabilities.setCapability("app", "com.apple.mobilesafari");
		capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.IOS_XCUI_TEST);

		// You MUST update the line below. This UDID will definitely be different.
		// Reference below to get your information.
		capabilities.setCapability(MobileCapabilityType.UDID, "A7575DED-3EB6-4E24-9D45-757AB4207DD5");

		// Display the output of the Xcode command used to run the tests
		capabilities.setCapability("showXcodeLog", true);

		URL conn = null;
		if (APPIUM_VERSION == 2) {
			// Appium V2 doesn't use /wd/hub
			conn = new URL("http://0.0.0.0:4723/");
		} else {
			conn = new URL("http://0.0.0.0:4723/wd/hub");
		}

		driver = new IOSDriver(conn, capabilities);
	}

	@AfterAll
	public static void afterAll() throws Exception {
		if (driver != null)
			driver.quit();
	}
	
	@Test
	@Order(1)  
	@DisplayName("Code-Gorilla Home Page Test")
	void codeGorillaNav() throws Exception {
		System.out.println("code-gorilla home page Test");
		String testUrl = "https://www.code-gorilla.com/";
		driver.get(testUrl);

		System.out.println("Presenting for 2 seconds...");
		TimeUnit.SECONDS.sleep(30);

		// This is a deliberate success to show on report.
		// This logo IS displayed on the target page.
		assertTrue(driver.findElement(By.id("code-gorilla-header-logo")).isDisplayed());
		System.out.println("Presenting for 1 seconds...");
		TimeUnit.SECONDS.sleep(1);
	}
	
	@Test
	@Order(3)  
	@DisplayName("Code-Gorilla Display Test")
	void codeGorillaDisplay() throws Exception {
		System.out.println("Code-Gorilla Display Test");
		String testUrl = "https://www.code-gorilla.com/view-article/appium-2.0-iphone-web-test-using-junit-5";
		driver.get(testUrl);

		System.out.println("Presenting for 5 seconds...");
		TimeUnit.SECONDS.sleep(5);

		// This is a deliberate failure to show on report.
		// This logo is NOT displayed on the target page.
		assertTrue(driver.findElement(By.id("code-gorilla-header-logo")).isDisplayed());
		System.out.println("Presenting for 3 seconds...");
		TimeUnit.SECONDS.sleep(3);
	}
	
	@Test
	@Order(2)  
	@DisplayName("USPS Browser Test")
	void zipCodeNav() throws Exception {
		System.out.println("USPS Browser Test");
		String testUrl = "https://tools.usps.com/zip-code-lookup.htm";
		driver.get(testUrl);

		System.out.println("Presenting for 3 seconds...");
		TimeUnit.SECONDS.sleep(3);

		WebElement element = driver.findElement(By.className("zip-code-cities"));
		if (element != null) {
			element.click();
		}

		System.out.println("Presenting for 3 seconds...");
		TimeUnit.SECONDS.sleep(3);

		System.out.println("Sending Zip Code characters...");
		WebElement zipText = driver.findElement(By.id("tZip"));
		zipText.sendKeys("18504");

		System.out.println("Presenting for 3 seconds...");
		TimeUnit.SECONDS.sleep(3);

		// Click on the submit button
		System.out.println("Clicking on button...");
		driver.findElement(By.id("cities-by-zip-code")).click();

		// Note: You need to wait to ensure the page is loaded to get the elements.
		System.out.println("Waiting for 3 seconds...");
		TimeUnit.SECONDS.sleep(3);

		System.out.println("Looking for a match...");
		boolean isFound = false;
		List cityList = driver.findElements(By.className("row-detail-wrapper"));
		if (cityList == null || cityList.size() < 1) {
			System.out.println("Couldn't find row-detail-wrapper items.");
		} else {
			for (WebElement cityName : cityList) {
				String cityText = cityName.getText();
				System.out.println("Comparing City: " + cityText);
				if (cityText.equalsIgnoreCase("SCRANTON PA"))
				{
					isFound = true;
				}
			}
		}
		assertTrue(isFound);
		System.out.println("Presenting for 3 seconds...");
		TimeUnit.SECONDS.sleep(3);
	}

}

Here is a high level overview of the code above:
  • The annotation @BeforeAll will be run before all of the tests. This is a good place to connect to your Appium server.
  • The annotation @AfterAll will run after all of the tests complete. This is a good place to clean up and disconnect from the Appium server.
  • This test is running locally, so the Appium 2.0 server address is http://0.0.0.0:4723/. If you were using Appium 1.0 it would be http://0.0.0.0:4723/wd/hub.
  • showXcodeLog is very useful when you're trying to figure out why things may not be working as it displays verbose logging during execution.
  • @Order(#) will determine the order the tests will run. This is optional and is only used when you want a specific order, otherwise would be in random order.
  • The @Order will not work without specifying @TestMethodOrder(MethodOrderer.OrderAnnotation.class)

References

 
Return to articles