Date: Thu, 28 Mar 2024 16:30:49 +0000 (UTC) Message-ID: <1149062407.10343.1711643449251@docs.getxray.app> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_10342_1872708053.1711643449250" ------=_Part_10342_1872708053.1711643449250 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html
In this tutorial, we will create a JUnit Test Case in Java, using the Appium<= /a> library for automation of iOS applications.
The following automated test is taken from the tutorials for iOS provide= d by Appium.
Please note
This example is found in the public Github repository<= /a> in https://github.com/appium= /tutorial/tree/master/projects/java_ios. It also provides examples= for other languages.
appium
The class implementing the automated tests needs to be updated in order = to properly set up the IP of the Appium server along with the required iOS = version.
package appium.tutorial.android.util; import appium.tutorial.android.page.HomePage; import com.saucelabs.common.SauceOnDemandAuthentication; import com.saucelabs.common.SauceOnDemandSessionIdProvider; import com.saucelabs.junit.SauceOnDemandTestWatcher; import com.saucelabs.saucerest.SauceREST; import io.appium.java_client.android.AndroidDriver; import org.apache.commons.logging.LogFactory; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.rules.TestRule; import org.junit.rules.TestWatcher; import org.junit.runner.Description; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.DesiredCapabilities; import java.io.File; import java.net.URL; import java.nio.file.Paths; import java.util.Date; import java.util.concurrent.TimeUnit; import static appium.tutorial.android.util.Helpers.driver; public class AppiumTest implements SauceOnDemandSessionIdProvider { static { // Disable annoying cookie warnings. // WARNING: Invalid cookie header LogFactory.getFactory().setAttribute("org.apache.commons.logging.Lo= g", "org.apache.commons.logging.impl.NoOpLog"); } /** Page object references. Allows using 'home' instead of 'HomePage' *= */ protected HomePage home; /** wait wraps Helpers.wait **/ public static WebElement wait(By locator) { return Helpers.wait(locator); } private boolean runOnSauce =3D System.getProperty("sauce") !=3D null; /** Authenticate to Sauce with environment variables SAUCE_USER_NAME an= d SAUCE_API_KEY **/ private SauceOnDemandAuthentication auth =3D new SauceOnDemandAuthentic= ation(); /** Report pass/fail to Sauce Labs **/ // false to silence Sauce connect messages. public @Rule SauceOnDemandTestWatcher reportToSauce =3D new SauceOnDemandTestWatcher= (this, auth, false); @Rule public TestRule printTests =3D new TestWatcher() { protected void starting(Description description) { System.out.print(" test: " + description.getMethodName()); } protected void finished(Description description) { final String session =3D getSessionId(); if (session !=3D null) { System.out.println(" " + "https://saucelabs.com/tests/" + s= ession); } else { System.out.println(); } } }; private String sessionId; /** Keep the same date prefix to identify job sets. **/ private static Date date =3D new Date(); /** Run before each test **/ @Before public void setUp() throws Exception { DesiredCapabilities capabilities =3D new DesiredCapabilities(); capabilities.setCapability("appium-version", "1.1.0"); capabilities.setCapability("platformName", "Android"); capabilities.setCapability("deviceName", "Android"); capabilities.setCapability("platformVersion", "7.1"); // Set job name on Sauce Labs capabilities.setCapability("name", "Java Android tutorial " + date)= ; String userDir =3D System.getProperty("user.dir"); URL serverAddress; String localApp =3D "api.apk"; if (runOnSauce) { String user =3D auth.getUsername(); String key =3D auth.getAccessKey(); // Upload app to Sauce Labs SauceREST rest =3D new SauceREST(user, key); rest.uploadFile(new File(userDir, localApp), localApp); capabilities.setCapability("app", "sauce-storage:" + localApp); serverAddress =3D new URL("http://" + user + ":" + key + "@onde= mand.saucelabs.com:80/wd/hub"); driver =3D new AndroidDriver(serverAddress, capabilities); } else { String appPath =3D Paths.get(userDir, localApp).toAbsolutePath(= ).toString(); capabilities.setCapability("app", appPath); serverAddress =3D new URL("http://127.0.0.1:4723/wd/hub"); driver =3D new AndroidDriver(serverAddress, capabilities); } sessionId =3D driver.getSessionId().toString(); driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); Helpers.init(driver, serverAddress); } /** Run after each test **/ @After public void tearDown() throws Exception { if (driver !=3D null) driver.quit(); } /** If we're not on Sauce then return null otherwise SauceOnDemandTestW= atcher will error. **/ public String getSessionId() { return runOnSauce ? sessionId : null; } }=20
The sample project contains two classes with the automated Tests. Below = is one of them:
package= appium.tutorial.ios; import appium.tutorial.ios.util.AppiumTest; import org.openqa.selenium.WebElement; import java.util.ArrayList; import java.util.List; import static appium.tutorial.ios.util.Helpers.*; public class AutomatingASimpleActionTest extends AppiumTest { @org.junit.Test public void one() throws Exception { text("Various uses of UIButton").click(); text_exact("Buttons"); } @org.junit.Test public void two() throws Exception { wait(for_text("Various uses of UIButton")).click(); wait(for_text_exact("Buttons")); } @org.junit.Test public void three() throws Exception { WebElement cell_1 =3D wait(for_text(2)); String page_title =3D cell_1.getAttribute("name").split(",")[0]; cell_1.click(); wait(for_text_exact(page_title)); } @org.junit.Test public void four() throws Exception { List<String> cell_names =3D new ArrayList<String>(); for (WebElement cell : tags("TableCell")) { cell_names.add(cell.getAttribute("name")); } for (String name : cell_names) { wait(for_text_exact(name)).click(); wait(for_text_exact(name.split(",")[0])); back(); } } }
Actually, the above class and others, such as Helpers.java, had to be ch= anged due to updates introduced by Apple in the automation API with XCUITes= t.
package appium.tutorial.ios.util; import io.appium.java_client.AppiumDriver; import io.appium.java_client.MobileElement; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.RemoteWebElement; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import java.util.ArrayList; import java.util.List; public abstract class Helpers { private static AppiumDriver driver; private static WebDriverWait driverWait; /** * Initialize the webdriver. Must be called before using any helper metho= ds. * */ public static void init(AppiumDriver webDriver) { driver =3D webDriver; int timeoutInSeconds =3D 60; // must wait at least 60 seconds for running on Sauce. // waiting for 30 seconds works locally however it fails on Sauce. driverWait =3D new WebDriverWait(webDriver, timeoutInSeconds); } /** * Wrap WebElement in MobileElement * */ private static MobileElement w(WebElement element) { return new MobileElement((RemoteWebElement) element, driver); } /** * Wrap WebElement in MobileElement * */ private static List<MobileElement> w(List<WebElement> element= s) { List list =3D new ArrayList(elements.size()); for (WebElement element : elements) { list.add(w(element)); } return list; } /** * Return an element by locator * */ public static MobileElement element(By locator) { return w(driver.findElement(locator)); } /** * Return a list of elements by locator * */ public static List<MobileElement> elements(By locator) { return w(driver.findElements(locator)); } /** * Press the back button * */ public static void back() { driver.navigate().back(); } /** * Return a list of elements by tag name * */ public static List<MobileElement> tags(String tagName) { return elements(for_tags(tagName)); } /** * Return a tag name locator * */ public static By for_tags(String tagName) { return By.className(tagName); } /** * Return a static text element by xpath index * */ public static MobileElement text(int xpathIndex) { return element(for_text(xpathIndex)); } /** * Return a static text locator by xpath index * */ public static By for_text(int xpathIndex) { //return By.xpath("//UIAStaticText[" + xpathIndex + "]"); return By.xpath("//XCUIElementTypeStaticText[" + xpathIndex + "]"); } /** * Return a static text element that contains text * */ public static MobileElement text(String text) { return element(for_text(text)); } /** * Return a static text locator that contains text * */ public static By for_text(String text) { String up =3D text.toUpperCase(); String down =3D text.toLowerCase(); //return By.xpath("//UIAStaticText[@visible=3D\"true\" and (contains(tr= anslate(@name,\"" + up return By.xpath("//XCUIElementTypeStaticText[@visible=3D\"true\" and (c= ontains(translate(@name,\"" + up + "\",\"" + down + "\"), \"" + down + "\") or contains(translate(@h= int,\"" + up + "\",\"" + down + "\"), \"" + down + "\") or contains(translate(@l= abel,\"" + up + "\",\"" + down + "\"), \"" + down + "\") or contains(translate(@v= alue,\"" + up + "\",\"" + down + "\"), \"" + down + "\"))]"); } /** * Return a static text element by exact text * */ public static MobileElement text_exact(String text) { return element(for_text_exact(text)); } /** * Return a static text locator by exact text * */ public static By for_text_exact(String text) { //XCUIElementTypeStaticText return By.xpath("//XCUIElementTypeStaticText[@visible=3D\"true\" and (@= name=3D\"" + text //return By.xpath("//UIAStaticText[@visible=3D\"true\" and (@name=3D\""= + text + "\" or @hint=3D\"" + text + "\" or @label=3D\"" + text + "\" or @value=3D\"" + text + "\")]"); } /** * Wait 30 seconds for locator to find an element * */ public static MobileElement wait(By locator) { return w(driverWait.until(ExpectedConditions.visibilityOfElementLocated= (locator))); } /** * Wait 60 seconds for locator to find all elements * */ public static List<MobileElement> waitAll(By locator) { return w(driverWait.until(ExpectedConditions.visibilityOfAllElementsLoc= atedBy(locator))); } }
Tests can be run using Maven.
mvn clean test
Since the previous command generates multiple JUnit XML files, we may ne= ed to merge them into a single XML file so it can be submitted into a Test = Execution more easily. That can be achieved by using the junit-merge utility.
junit-merge -o results.xml -d target/surefire-reports/
After successfully running the Test cases and generating the aggregated = JUnit XML report (e.g., results.xml), it can be imported to Xray (either = by the REST API or through the Import Execution Results ac= tion within the Test Execution).
Each JUnit's Test Case is mapped to a Generic Test in Jira, and the
The Execution Details of the Generic Test contains information about the= Test Suite, which in this case corresponds to the Test Case class, includi= ng its namespace.