Appium : Different locator IDs for the same element in different phones? Read on…

Anurut Malhan
noobQA
Published in
4 min readJul 19, 2019

So, you’ve written a test and it works flawlessly on your test device. But when you try it on other phones you get a test failure. Why? Because the locator ID for a specific element is different for different phone manufacturers (Android). One such example is the clear all notifications button in the Notification panel.

Locators for 'clear all' button on different phonesFor OnePlus phones : com.android.systemui:id/clear_notificationsFor Xiaomi phones : com.android.systemui:id/dismiss_viewFor Samsung phones : com.android.systemui:id/clear_all_button_label             

How do you implement a generalised version of your code so that it works on every phone?

Well there are two approaches to it:

  • The try / catch approach
  • The Page Factory approach

The try / catch approach

You can try this approach by including the elements for the button locator in try/catch blocks and return a value of the element locator which is found on the screen.

public String whichPhonesElement() {try {if(driver.findElement(By.id("com.android.systemui:id/clear_notifications")).isDisplayed()){System.out.println();System.out.println("Found OnePlus phone");return "oneplus";}} catch (NoSuchElementException e) {System.out.println();System.out.println("Not a OnePlus phone " + e);}try {if(driver.findElement(By.id("com.android.systemui:id/dismiss_view")).isDisplayed()){System.out.println();System.out.println("Found Xiaomi phone");return "xiaomi";}} catch (NoSuchElementException e) {System.out.println();System.out.println("Not a Xiaomi phone " + e);}try {if(driver.findElement(By.id("com.android.systemui:id/clear_all_button_label")).isDisplayed()){System.out.println();System.out.println("Found a Samsung phone");return "samsung";}} catch (NoSuchElementException e) {System.out.println();System.out.println("Not a Samsung phone " + e);}try {if(driver.findElement(By.id("com.android.systemui:id/clear_all")).isDisplayed()){System.out.println();System.out.println("Found a Samsung phone");return "samsung";}} catch (NoSuchElementException e) {System.out.println();System.out.println("Not a Samsung phone " + e);}return "";}

Now we just have to use the method above in a switch case:

public void clickClearAllButton() {switch (whichPhonesElement()) {case "oneplus":AndroidElement clearAllbtn = driver.findElement(By.id("com.android.systemui:id/clear_notifications");if (clearAllbtn.isEnabled()) {clearAllbtn.click();}break;case "xiaomi":AndroidElement clearAllbtn = driver.findElement(By.id("com.android.systemui:id/dismiss_view"));if (clearAllbtn.isEnabled()) {clearAllbtn.click();}break;case "samsung":AndroidElement clearAllbtn = driver.findElement(By.id("com.android.systemui:id/clear_all_button_label"));if (clearAllbtn.isEnabled()) {clearAllbtn.click();}break;default:System.out.println("None of the expected phones are available");break;}}

Update the methods above for each unique locator ID and wallah!, your code is working great!

Although this is quite an easy approach towards the problem there are pros and cons to this approach.

Pros :

  • This is a very basic and beginner friendly approach.
  • It is faster than the next approach we’ll discuss about.

Cons :

  • Not easy to maintain. The tester will have to update at at least two different places for any update in the locator IDs.
  • More number of lines of code for a small feature.

The Page Factory approach

If you are familiar with pagefactory then this is quite an easy approach. All you have to do is to use LocatorStrategy

First you need to save all the locators in a class named ‘Constants’

public class Constants {//Notification Bar Constantspublic static final String NOTIFICATION_SCREEN_CLEAR_NOTIFICATION = "com.android.systemui:id/clear_notifications";public static final String NOTIFICATION_SCREEN_CLEAR_NOTIFICATION_MI = "com.android.systemui:id/dismiss_view";public static final String NOTIFICATION_SCREEN_CLEAR_NOTIFICATION_SAMSUNG = "com.android.systemui:id/clear_all_button_label";}

In your screen class you need to implement pagefactory

@HowToUseLocators(androidAutomation = LocatorGroupStrategy.ALL_POSSIBLE)
@AndroidFindBy(id = Constants.NOTIFICATION_SCREEN_CLEAR_NOTIFICATION_SAMSUNG)//Samsung
@AndroidFindBy(id = Constants.NOTIFICATION_SCREEN_CLEAR_NOTIFICATION_MI) //Xiaomi
@AndroidFindBy(id = Constants.NOTIFICATION_SCREEN_CLEAR_NOTIFICATION) //OnePlus
private static List<AndroidElement> clearNotif;

If you observe the data type of the variable clearNotif, it is of type ‘List’, which means that the LocatorGroupStrategy.ALL_POSSIBLE returns a list of all the elements found from the given elements. To use that element we have to use

clearNotif.get(0)

as there will always be only one element in this scenario. In order to click on the element all you need to do is

clearNotif.get(0).click();

Now the NotificationScreen class should look something like this

import java.util.List;
import org.testng.Assert;
import base.Constants;
import base.ScreenBase;
import utils.commonUtils;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.AndroidElement;
import io.appium.java_client.pagefactory.AndroidFindBy;
import io.appium.java_client.pagefactory.HowToUseLocators;
import io.appium.java_client.pagefactory.LocatorGroupStrategy;
public class NotificationScreen extends ScreenBase {public NotificationScreen(AndroidDriver<AndroidElement> driver) {super(driver);}// Finding Elements@HowToUseLocators(androidAutomation = LocatorGroupStrategy.ALL_POSSIBLE)
@AndroidFindBy(id = Constants.NOTIFICATION_SCREEN_CLEAR_NOTIFICATION_SAMSUNG)//Samsung
@AndroidFindBy(id = Constants.NOTIFICATION_SCREEN_CLEAR_NOTIFICATION_MI) //Xiaomi
@AndroidFindBy(id = Constants.NOTIFICATION_SCREEN_CLEAR_NOTIFICATION) //OnePlus
private static List<AndroidElement> clearNotif;
public void clearNotification() throws InterruptedException {System.out.println("");
System.out.println("---------- Inside clearNotification() ----------");
driver.openNotifications();
System.out.println("---------- Opened Notifications ----------");
try {AndroidElement notif = clearNotif.get(0);System.out.println(notif);if (notif.isEnabled()) {notif.click();
System.out.println("---- Clicked on clear all button ----");
} else if(!notif.isEnabled()) {System.out.println("No notifications to clear, going back");
clickBackButton();
}} catch (Exception e) {System.out.println(e);
System.out.println("No notifications to clear, going back");
driver.pressKey(new KeyEvent(AndroidKey.BACK));}
}
}

Don’t forget to implement pagefactory in the base class of your framework

public void loadElements() {
PageFactory.initElements(new AppiumFieldDecorator(driver), this);
}
//Constructor
public ScreenBase(AndroidDriver<AndroidElement> driver) {
ScreenBase.driver = driver;
loadElements();//for @AndroidFindBy
}

That’s it, super easy right? Now this approach also has its pros and cons, which I’ve listed below.

Pros :

  • This approach is easier to maintain as any changes will only have to be made in the ‘LocatorStrategy’ block only.
  • Less lines of code.

Cons :

Its execution is time consuming as compared with ‘the try/catch approach’ . How?

Let’s see. Let’s assume you have used implicit wait of 5 seconds. In the try/catch approach as soon as it finds the element it will return and continue forward e.g. if it finds the second element hence the wait time will be only 5 seconds (for the first element only as it’ll return as soon as it finds the second element).

In pagefactory approach it will try and find all the elements listed. It doesn’t matter if it finds the first element or the last element. It will look for all the elements, hence, if there are four elements listed then it will always take 4*5=20 seconds to return the element.

If you found this post interesting then you might also like these posts as well : Fetching OTP from the notification panel using Appium and How to handle the scenario where user gets different main screen every time he/she opens the application.

And if you liked this article then send some claps my way, all I want is some applause :)

--

--