Automation testing is an integral part of any successful product development. We also discussed it in our previous blog on Jenkins.

While architecting, developing, and scaling a large USA based SaaS product, having multiple apps (both web & mobile), we automated testing for one of the mobile application using Appium too. In this article, I would like to share a specific challenge we faced with Appium and UiAutomator and how we resolved it.

Setup with Appium v1.15.1, Emulator v30.0.5 & UiAutomator 1

We installed required software, i.e. Appium, sdkmanager command line tool, Emulator, etc.

  • Appium - v1.15.1
  • Emulator - v30.0.5

On completion of the setup, the first required script for any application is the login script. And further we moved on to automate other key user flows.

Upgrade to the latest version

It's always a good idea to use the latest version of the software. We also upgraded Appium and Emulator to the following versions:

  • Appium - v1.17.1
  • Emulator - v30.0.12

Problem

On running the login script, we came across a failure. This was quite confusing as there were no changes in the script. Our only concern was How only upgrading Appium to the latest version can cause the script to fail? But, the script was running fine on another machine which had Appium v1.15.1.

Root Cause Investigation

After seeing this weird behavior, we decided to downgrade Appium to the older version v1.15.1. But even after downgrading to the older version, it was giving the same error.

To find the root cause of it, we upgraded the Appium version on another machine as well. But on another machine, the script was working fine even after we upgraded the Appium version to v1.17.1.

We understood that it is the Emulator's upgraded version due to which script is failing. Because another machine had the Emulator of version v30.0.5.

Login Script

The script was actually logging in to the application. But couldn't locate element in the Home screen without some manual intervention. We were using UiAutomator1 to execute commands on an emulator.

Below is the code snippet of login function:

public static void login() throws DocumentException, IOException, InterruptedException {
 WebDriverWait wait = new WebDriverWait(driver, 50);
 String userId = "abc";
 String password = "password";

 MobileElement usernameField = (MobileElement) Configurations.wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("userNameFieldXpath")));
 MobileElement passwordField = (MobileElement) Configurations.wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("passwordFieldXpath")));
 MobileElement signInBtn = (MobileElement) Configurations.wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("signInBtnXpath")));

 usernameField.sendKeys(userId);
 passwordField.sendKeys(password);
 signInBtn.click();

 Utility.logger.log(Status.INFO, userId + " logged in");
 Configurations.wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("homeLabelXpath")));
}	

Script was unable to locate the element homeLabelXpath. Below was the error which we were getting:

Is it a wait issue...?? 🤔

We added implicit wait in the code:

driver.manage().timeouts.implicitlyWait(TimeUnit.SECONDS, 30);

But the above code didn't resolve the issue.

Got to know about the screen loading issue...

We tried a workaround to overcome the issue on the home page. The workaround was to re-launch the app and continue from the Home screen of the app.

First, we tried using driver.runAppInBackground(secs) function. But this was actually relaunching the app from the login screen. But, we needed the app to continue from the Home screen of the app. This did not work.

Then we tried to relaunch the app from the recent apps. We pressed the Home key, then the Recent Apps key, and finally switched to the app.

After this, the script was able to locate homeLabelXpath. Moving to the other screens of the app, the script was not able to locate elements. It again took some manual intervention to run ahead. We realized that whenever we load any screen, UiAutomator1 cannot locate the element.

Now when we understood the problem, so this workaround did not seem to be a correct solution. As for every screen load if we relaunch the app by pressing the Home key, then that is also time consuming.

Finally got a solution!!!

After trying a few workarounds like runAppInBackground() and relaunching the app by pressing Home key, we finally decided to raise an issue on Appium GitHub Repository.

After raising the issue, we got to know that UiAutomator1 is obsolete now for Android 6+. We had set the following desired capabilities for our Appium:

DesiredCapabilities caps = new DesiredCapabilities();
caps.setCapability(MobileCapabilityType.AUTOMATION_NAME, "UiAutomator1");
caps.setCapability(CapabilityType.PLATFORM_NAME, "ANDROID");
caps.setCapability(MobileCapabilityType.PLATFORM_VERSION, "9.0 (Pie) - API 28");
caps.setCapability(MobileCapabilityType.DEVICE_NAME, "AOSP on IA Emulator");
caps.setCapability(MobileCapabilityType.UDID, "emulator-5554");
caps.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, "60");
caps.setCapability(MobileCapabilityType.APP, Configurations.getData("app", "com.myapp.qa");
caps.setCapability("appActivity","com.myapp.views.activities.SplashScreenActivity");
caps.setCapability("unicodeKeyboard", true);

We switched to UiAutomator2 and updated our desired capabilities to below code:

String apkPath = "src/main/java/Resources/" + apkFile;
File app = new File(apkPath);
DesiredCapabilities caps = new DesiredCapabilities();
caps.setCapability(MobileCapabilityType.AUTOMATION_NAME, "UiAutomator2");
caps.setCapability(CapabilityType.PLATFORM_NAME, "ANDROID");
caps.setCapability(MobileCapabilityType.PLATFORM_VERSION, "9.0 (Pie) - API 28");
caps.setCapability(MobileCapabilityType.DEVICE_NAME, "AOSP on IA Emulator");
caps.setCapability(MobileCapabilityType.UDID, "emulator-5554");
caps.setCapability(MobileCapabilityType.APP, app.getAbsolutePath());
caps.setCapability("appActivity", "com.myapp.views.activities.SplashScreenActivity");
caps.setCapability("appWaitActivity", "*");
caps.setCapability("unicodeKeyboard", true);
caps.setCapability(MobileCapabilityType.NO_RESET, false);
caps.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 360);

With this change, our issue got resolved and all worked very well 😍.

Thanks for reading.