Validating Android Toast Messages with Appium

Toast

     As we know that Android Toast is a small message displayed on the screen, similar to a tooltip or other similar popup notification. A Toast is visible for a short time period. If you’ve used Android apps for any length of time, you’ve no doubt noticed these little notifications that pop up and fade away with time.

     Of course, toast messages can prove a challenge for automation. From the perspective of the Android Accessibility layer, toast messages aren’t visible! If you try to get the XML source from an Appium session while a toast is present on the screen, you won’t find its text anywhere. Luckily, with the advent of the Espresso driver (don’t forget to use the capability caps.setCapability(“automationName”, “Espresso”);), we have the ability to match text against on-screen toasts. In this article, I would like to share how it all works.

      Once the toasts message showing up, we need a way to check what they say for verification. Unfortunately, we don’t have a method for getting the text from a toast message. What we have instead is a method that takes a string and tells us whether the on-screen toast matches that string or not. This is enough for our purposes of verification. So let’s check out how to use the mobile: isToastVisible method:

ImmutableMap<String, Object> args = ImmutableMap.of(
“text”, “toast text to match”,
“isRegexp”, false
);
driver.executeScript(“mobile: isToastVisible“, args);

     Like all mobile: methods, we first need to construct a map of our arguments. This method takes two parameters: the text we want to look for, and a flag which tells Appium whether this text is in the form of a simple string or a regular expression. Finally, we call executeScript as a way of accessing the mobile: method.

     As we know that toast messages are a time-sensitive phenomenon. So we probably want to start looking for a matching toast before it pops up, so we’re sure we don’t miss it. To this end, we can use a custom Explicit Wait. It’s possible to use the Java client’s ExpectedCondition interface to define our own custom expected conditions. Here’s a helper method that defines a new ExpectedCondition and you can use this method in your automation script to match the toast message.

public static ExpectedCondition<Boolean> toastMatches(String matchText, Boolean isRegexp) {
return new ExpectedCondition<Boolean>() {
@Override
public Boolean apply(WebDriver driver) {
ImmutableMap<String, Object> args = ImmutableMap.of(
“text”, matchText,
“isRegexp”, isRegexp
);
return (Boolean) ((JavascriptExecutor)driver).executeScript(“mobile: isToastVisible”, args);
}

@Override
public String toString() {
return “toast to be present”;
}
};
}

     All we do is override the appropriate methods of the ExpectedCondition class, and ensure we have appropriate typing in a few places, and we’ve got ourselves a nice self-contained way of waiting for toast messages, in conjunction with (for example) WebDriverWait. In reality, you can use the following snippet format within your test script to validate the toast messages in Android,

WebDriverWait wait = new WebDriverWait(driver, 10);
//Application should generate the toast message
wait.until(toastMatches(“simple toast message”, false));
wait.until(toastMatches(“regular expression of toast message”, true));

     We define a WebDriverWait and use it with our toastMatches condition. You can see that we perform a match with both available modes, first by matching the exact toast string, and secondly by using a regular expression, highlighting how we could verify the presence of a valid toast message even if it contains dynamically generated content. Try to use the above code snippet along with your Android automation to validate the toast messages on the screen.

Reference: Appium Pro

make it perfect!

Espresso vs UiAutomator2 in Appium

maxresdefault

     Espresso is an Android test automation library maintained by Google. It has a number of advantages, for example built-in view synchronization that ensures element finding happens during idle periods in your app. Most people assume Espresso is an alternative to Appium; you’d pick either Appium or Espresso, not both. From this perspective, there’s nothing odd about creating an Appium Espresso driver. And that is exactly what we’ve done. For some time we’ve been working on a very rough beta (really alpha) version of an Appium driver that runs Espresso under the hood. This means the same kind of Appium script you’re used to writing, but running on Google’s top-tier automation technology.

     In essence, it’s pretty simple: we just change the automationName capability to Espresso (instead of, say, UiAutomator2 if you’re using the current standard Android driver for Appium). By designating this automation name, Appium will know to start an Espresso session instead of something else.

     You will get the Espresso driver in Appium by installing the new version of Appium (npm install -g appium@beta).

     The best way to show off what you can currently do with the Espresso beta is with a comparison. The code for this article is therefore the same test (of a basic login flow) run on both UiAutomator2 and Espresso drivers. Let’s take a look at the code for the standard UiAutomator2 driver first:

@Test
public void testLogin_UiAutomator2() throws MalformedURLException {
    AndroidDriver driver = getDriver(“UiAutomator2“);
    WebDriverWait wait = new WebDriverWait(driver, 10);
    ExpectedCondition loginScreenReady =
    ExpectedConditions.presenceOfElementLocated(loginScreen);
    ExpectedCondition usernameReady =
    ExpectedConditions.presenceOfElementLocated(username);
    ExpectedCondition verificationReady =
    ExpectedConditions.presenceOfElementLocated(verificationTextUiAuto2);
 
    try {
        wait.until(loginScreenReady).click();
        wait.until(usernameReady).sendKeys(“sanoj”);
        driver.findElement(password).sendKeys(“mypassword”);
        driver.findElement(loginBtn).click();
        wait.until(verificationReady);
    } finally {
        driver.quit();
    }
}

     The first thing to observe about this snippet is that we have a helper method, getDriver, which simply takes the automation name and gets us an instance of AndroidDriver. This is so we can reduce code duplication when we do the same thing for the Espresso version of the test (and to show that all the capabilities are the same, other than automationName). Next, we set up 3 expected conditions for user later on in the test. Finally, our test flow itself is 5 steps long, utilizing pre-defined locator fields like password. The steps includes: (1) get to the login prompt, (2) enter the username, (3) enter the password, (4) tap the log in button, and (5) verify that an element with the correct logged-in text is present.

So far, so good! Now let’s take a look at the same test, but written for the Espresso driver:

@Test
public void testLogin_Espresso() throws MalformedURLException {
    AndroidDriver driver = getDriver(“Espresso“);
    WebDriverWait wait = new WebDriverWait(driver, 10);
    ExpectedCondition loginScreenReady =
    ExpectedConditions.presenceOfElementLocated(loginScreen);
 
    try {
        wait.until(loginScreenReady).click();
        driver.findElement(username).sendKeys(“sanoj”);
        driver.findElement(password).sendKeys(“mypassword”);
        driver.findElement(loginBtn).click();
        driver.findElement(verificationTextEspresso);
    } finally {
        driver.quit();
    }
}

Can you spot the differences? There are just two:

1. We only need 1 explicit wait instead of 3.

2. We have a different verification element we’re looking for in the last step.

     Otherwise, the test code is exactly the same! This is great, because it means that, for the most part, changes were not required to migrate this particular test to the Espresso driver. Now, why do we only need 1 explicit wait instead of 3 as before? We needed them in the UiAutomator2 example because any time we try to find an element after a view transition, we have no guarantees about the timing of when the new view will show up, and we have to hedge our bets with an explicit wait. One of the benefits of Espresso, however, is synchronization, which as I explained before means that Espresso itself will hold off on finding any elements until it believes the app is in an idle state. What this means is that, for the most part, we don’t need to worry about waits in Espresso! (We do still need the first wait because synchronization is not in effect until the app itself is fully loaded and instrumented by Espresso, and Appium doesn’t know exactly when that happens).

Try to use the Espresso in your Android Automation and enjoy.

make it perfect !

Reference: Appium Pro.