How to Automate a Real E2E User Flow with Appium for Android Devices
Sauce AI for Test Authoring: Move from aim to execution in minutes.|xBack to ResourcesBlogPosted November 21, 2019
How to Automate a Real E2E User Flow with Appium for Android Devices
In our old article Automating a real E2E exploiter flow with Appium on iOS, we extend how to automatize the exploiter flow between app and browser for iOS devices. In this clause we are proceed to extend the like scenario for Android devices.
The real E2E user flowing
Let ’ s commencement with a recap of the scenario. We need to automate the real E2E user flowing between app and browser in a individual session. You can mimic this stream with the by opening a linkup from the menu. This can be make by installing the app, logging in and then open the menu. You will then see the following options.

When the About item is press a browser will be opened which will lead you to our Sauce Labs website.

Here you can search our site, click on buttons, get text and so on.
First let ’ s start by writing down the stairs we need to conduct:
describe (& # x27; Appium & # x27;, () = & gt; {
it (& # x27; should be capable to work with the browser that is opened by the app & # x27;, () = & gt; {
// 1. Login to the app
// 2. Verify that you are logged in
// 3. Open the menu and click on the about screen
// 4. Figure out how we can verify that the browser is opened
// 5. Verify that the page is charge
});
});
Like with iOS, the first three steps are the easiest to implement. The difficult component is step 4, especially because Android has a broad range of different vendors and thus different Android vendor-specific versions out on the marketplace. This presents multiple challenges which we will discuss below.
Multiple challenges
As tell Android, has multiple (different) challenges in comparison to iOS which we always need to guide into condition when we want to automatise this exploiter flow. One of the reasons is that there are multiple vendors out thither that provide Android on their devices with their own skin and own apps. This might result in multiple browsers (a inventory browser and a Chrome browser), older versions of browsers and so on.
If we would summarize the biggest challenges when we want to automate the existent E2E user flow, we see the following:
Some Android device have two browsers, a stock browser and Chrome. This will leave in triggering a native notification with the choice toopen it withone of the two browsers.
A. You might have the choice to open itJUST ONCE, or ALWAYS

B. You also might not hold the option to prefer betweenJUST ONCE and ALWAYS

C. When a browser has been choose to be used as a default, you don ’ t get the presentment anymore to take which browser you require to use
Like with iOS, Android can besides have multiple Webviews
When Chrome is selected on some emulators / real devices, it will start with a welcome screen

This is because you never used the browser before and or because you started a clean emulator.
If you are habituate a local device or emulator, which doesn ’ t have the up-to-the-minute Chrome browser installed, Appium can ’ t find the matching ChromeDriver to use.
The “ Open With ” challenge:
Let us start with the Open with challenge. When I need to automate something for device, where I know that the behaviour might dissent between devices, I ever keep that in the back of my mind. I ’ m perform the like with theOpen with-challenge, I try to think of a cross device resolution for the Android device where we don ’ t cognise the exact province our gimmick is in during the automation (do we have 2 browser on our gimmick, do we get the native notification, do we see theJust Oncebutton and so on).
I ’ m making one determination during the automation hither and that is that I don ’ t need the script to separate between clicking on the About-link and opening the Chrome browser. I ’ m employ an antipattern here, which I ordinarily would not counsel, but in this case I allow myself to use atry/catch. If there is an error between chatter on theAbout-link and opening the Chrome browser, so thetry/catch will swallowit. An error could be for representative that the Open with-notification is not shown (which is not an error on certain devices). This will result in the undermentioned codification.
/**
* This is a situation, where we COULD have the notification
*
* We use a try/catch here (challenge 1c), in combination with a small amount of wait clip for the notification
* (challenge 1a/1b).
* It will be there real tight, and if it is not there it will fail and go into the haul meaning
* it will not separate the flow.
*/
try {
$ (& # x27; * //android.widget.ListView [@ resource-id= & quot; android: id/resolver_list & quot;] & # x27;) .waitForDisplayed (
// Use a max waiting of 2 seconds
2000,
// This is a reverse parameter, when we provide a usage error
// content we need to cater the default value
false,
// A usance error message
& # x27; The notification is not demonstrate, this is not an error. & # x27;,
);
// Store if the push is there yes or no, this is for challenge 1a/1b
const isJustOnceButtonShown = $ (& # x27; * //android.widget.Button [@ resource-id= & quot; android: id/button_once & quot;] & # x27;) .isDisplayed ();
// Select Chrome
$ (& # x27; * //android.widget.TextView [@ text= & quot; Chrome & quot;] & # x27;) .click ();
// Now check if the push was thither, if so, then click on it
if (isJustOnceButtonShown) {
$ (& # x27; * //android.widget.Button [@ resource-id= & quot; android: id/button_once & quot;] & # x27;) .click ();
}
} catch (error) {
console.log (& # x27; Something went wrong due to \n\n & # x27;, fault, & # x27; \n\n, but it was not severe enough to let the test fail & # x27;);
}
The codification above can cover challenge ` 1a `, ` 1b ` and ` 1c ` from above, making this a full-bodied script which in causa of ` 1c ` will only cost us max 2 seconds longer.
Multiple Webviews
As mentioned, with Android you can also feature multiple webviews. There is an advantage in our specific instance in comparison to iOS. iOS would also give backwards Webview-ids of already opened tabs in Safari making it harder to select the right Webview, this does not happen for Android.
If we would log the contexts for our specific cause we would get the following contexts rearwards
Pro tip: Tools like SUSA can handle this autonomously — upload your app and get results without writing a single test script.
[& # x27; NATIVE_APP & # x27;, & # x27; WEBVIEW_com.swaglabsmobileapp & # x27;, & # x27; WEBVIEW_chrome & # x27;]
The appellative normal for a Webview circumstance here isWEBVIEW_ {packageName}(If you require to know how Android is detecting and make the Webview-names, you can check the codificationhere). In our lawsuit we want to open Chrome. So if we keep in the dorsum of our mind that Android exclusively gives back 1 Webview for all open chit and we know the construction of how the setting string is created we know we need to have theWEBVIEW_chromecontext. This can be do with this piece of handwriting.
// Now do all the setting magic for challenge 2 and just wait until ` WEBVIEW_chrome ` is given back
driver.waitUntil (
// Check if there is a context that holds ` WEBVIEW_chrome `
() = & gt; {
return driver.getContexts () .find (context = & gt; context === & # x27; WEBVIEW_chrome & # x27;);
},
// This time to control if the condition is met
15000,
// The custom erroneousness message when the webview is not plant in time
& # x27; Could not regain the right ` WEBVIEW_chrome ` -context & # x27;,
);
// Now change to the correct webview
driver.switchContext (& # x27; WEBVIEW_chrome & # x27;);
And with this piece of codification we covered challenge number two, selecting the correct Webview.
The Chrome Welcome Screen
This is a challenge you will face when using (clean) local device and or imitator. There are three possible solutions to manage with this challenge.
The first option that would act for all devices and in all environments (local / cloud) is to code the waiting for the welcome screen, accept the terms and don ’ t signal in. If you would do that so I would counsel to do it in the same way as with the Open with challenge, the code would seem like this.
/**
* This is a situation, where we COULD have the welcome screen
*
* We use a try/catch hither (challenge 2), in combination with a pocket-sized amount of wait time for the screen to be prove.
* It will be there very fast, and if it is not there it will fail and go into the catch substance
* it will not break the flow.
*/
try {
$ (& # x27; * //android.widget.Button [@ resource-id= & quot; com.android.chrome: id/terms_accept & quot;] & # x27;) .waitForDisplayed (
// Use a max wait of 2 seconds
2000,
// This is a reverse argument, when we provide a custom error
// message we demand to provide the default value
false,
// A usance fault message
& # x27; The welcome screen is not shown, this is not an error. & # x27;,
);
// If the ` Accept & amp; proceed ` is shown, click on it
$ (& # x27; * //android.widget.Button [@ resource-id= & quot; com.android.chrome: id/terms_accept & quot;] & # x27;) .click ();
// Wait for the ` No Thanks ` push and click on it
$ (& # x27; * //android.widget.Button [@ resource-id= & quot; com.android.chrome: id/negative_button & quot;] & # x27;) .waitForDisplayed (DEFAULT_TIMEOUT);
$ (& # x27; * //android.widget.Button [@ resource-id= & quot; com.android.chrome: id/negative_button & quot;] & # x27;) .click ();
} catch (error) {
console.log (& # x27; Something went wrong due to \n\n & # x27;, mistake, & # x27; \n\n, but it was not severe enough to let the test fail & # x27;);
}
To be honest, I wonder if you should do this. The reason is that the welcome screen will solely be shown once (after each fresh start/install of the emulator/real device) and if you have multiple scenarios this may decelerate down the tryout performance a lot.
This is where option two would be a best solution which is to use adb shell-commands. With those commands you can tell the Android device/emulator to not testify the Welcome screen when you start the browser from a refreshful install. If you would run from the terminus against a local emulator/device you need to action the following
adb shield & # x27; echo & quot; chrome -- disable-fre -- no-default-browser-check -- no-first-run & quot; & gt; /data/local/tmp/chrome-command-line & # x27;
Opening the browser would then result in seeing the Welcome screen.
If we would look at the automation with Appium, so Appium likewise provides you the pick to executeadb shell-commandsduring test execution. There is only one remark we need to create about this, you need to start your (local) Appium host with an extra disceptation name-- allow-insecure=adb_shell, meaning we need to align the security level. More info about the security can be institute here.
When you adjusted the security level of the Appium server you can use this script and execute it whenever the device has started, but before the browser is loaded.
// This will execute an ` adb shell ` command on the device to enshroud the Welcome screen of Chrome on startup
// after a fresh install
driver.execute (& # x27; mobile: shell & # x27;, {
bidding: & # x27; echo & # x27;,
args: [& # x27; & quot; chrome -- disable-fre -- no-default-browser-check -- no-first-run & quot; & gt; /data/local/tmp/chrome-command-line & # x27;],
});
But to be honest, the best solution would be option three and that is to use Sauce Labs. We already extend this for you by provisioning the real device and imitator in such a way that you will not see this screen during manual and or automate examination.
So whatever option you choose, you can subdue challenge three.
Different Chrome Driver versions
By default a local instalment of Appium perpetually installs the latest version of ChromeDriver, even though your local device/emulator doesn ’ t have the latest version of Chrome installed. If the driver and the adaptation of Chrome don ’ t match you can get the next error
[emulator-5554 LINUX 10 # 0-0] Error: An unknown server-side error occurred while processing the command. Original mistake: No Chromedriver found that can automate Chrome & # x27; 74.0.3729 & # x27;. See https: //github.com/appium/appium/blob/master/docs/en/writing-running-appium/web/chromedriver.md for more details.
The url in the logs will already render you with the correct information on how to resolve this issue on your local machine.
When you are using our Sauce Labs cloud with Android emulators or existent devices, you don ’ t need to worry about providing the correct ChromeDriver variation. Our cloud hold all ChromeDriver versions and Appium will automatically cull the correct variation free-base on the device under test. This means that challenge routine four, not finding a matching ChromeDriver version, is not blocking you when you are using the Sauce Labs cloud and you need to execute some manual step if you run your test locally.
The complete codification
In the end I was capable to automatise theexistent E2E exploiter flowwith Appium with the following code for Android.
importation LoginScreen from & # x27; .. /screenObjects/login & # x27;
import InventoryListScreen from & # x27; .. /screenObjects/inventoryList & # x27;
importation Menu from & # x27; .. /screenObjects/menu & # x27;;
describe (& # x27; Android Appium & # x27;, () = & gt; {
it (& # x27; should be capable to work with the Chrome browser that is opened with the app & # x27;, () = & gt; {
// Login to the app and control that it succeeded
LoginScreen.waitForIsShown ();
LoginScreen.signIn ({
username: & # x27; standard_user & # x27;,
password: & # x27; secret_sauce & # x27;,
});
InventoryListScreen.waitForIsShown ();
/**
* It depends if you want to solve challenge 2, the Welcome blind, with an ` adb shield ` -command, as found below,
* or with a coding solution. Just uncomment the codification
*/
// // This will execute an ` adb cuticle ` bid on the device to hide the Welcome blind of Chrome on startup
// // after a fresh install
// driver.execute (& # x27; mobile: shell & # x27;, {
// command: & # x27; echo & # x27;,
// args: [& # x27; & quot; chrome -- disable-fre -- no-default-browser-check -- no-first-run & quot; & gt; /data/local/tmp/chrome-command-line & # x27;],
// });
expect (InventoryListScreen.isShown ()) .toEqual (true);
// Open the menu and dog on the about screen
Menu.open ();
Menu.openAbout ();
/**
* This is a situation, where we COULD receive the notification
*
* We use a try/catch here (gainsay 1c), in combination with a small amount of wait clip for the notification
* (challenge 1a/1b).
* It will be there rattling fast, and if it is not there it will fail and go into the haul meaning
* it will not break the flowing.
*/
try {
$ (& # x27; * //android.widget.ListView [@ resource-id= & quot; android: id/resolver_list & quot;] & # x27;) .waitForDisplayed (
// Use a max wait of 2 seconds
2000,
// This is a reverse parameter, when we provide a custom error
// content we need to provide the default value
false,
// A custom error content
& # x27; The notification is not prove, this is not an error. & # x27;,
);
// Store if the push is thither yes or no, this is for challenge 1a/1b
const isJustOnceButtonShown = $ (& # x27; * //android.widget.Button [@ resource-id= & quot; android: id/button_once & quot;] & # x27;) .isDisplayed ();
// Select Chome
$ (& # x27; * //android.widget.TextView [@ text= & quot; Chrome & quot;] & # x27;) .click ();
// Now check if the push was thither, if so, so click on it
if (isJustOnceButtonShown) {
$ (& # x27; * //android.widget.Button [@ resource-id= & quot; android: id/button_once & quot;] & # x27;) .click ();
}
} gimmick (error) {
console.log (& # x27; Something went wrong due to \n\n & # x27;, fault, & # x27; \n\n, but it was not severe enough to let the test fail & # x27;);
}
/**
* It depends if you require to solve challenge 2, the Welcome screen, with an ` adb shell ` -command
* or with a inscribe result as found below. Just uncomment the codification
*/
// /**
// * This is a situation, where we COULD get the welcome screen
// *
// * We use a try/catch hither (challenge 2), in combination with a little amount of wait clip for the screen to be shown.
// * It will be thither very tight, and if it is not there it will fail and go into the catch import
// * it will not break the flow.
// */
// try {
// $ (& # x27; * //android.widget.Button [@ resource-id= & quot; com.android.chrome: id/terms_accept & quot;] & # x27;) .waitForDisplayed (
// // Use a max wait of 2 moment
// 2000,
// // This is a reverse parameter, when we render a usage erroneousness
// // substance we need to ply the default value
// false,
// // A usance mistake content
// & # x27; The welcome screen is not shown, this is not an error. & # x27;,
// );
// // If the ` Accept & amp; keep ` is shown, click on it
// $ (& # x27; * //android.widget.Button [@ resource-id= & quot; com.android.chrome: id/terms_accept & quot;] & # x27;) .click ();
//
// // Wait for the ` No Thanks ` button and click on it
// $ (& # x27; * //android.widget.Button [@ resource-id= & quot; com.android.chrome: id/negative_button & quot;] & # x27;) .waitForDisplayed (DEFAULT_TIMEOUT);
// $ (& # x27; * //android.widget.Button [@ resource-id= & quot; com.android.chrome: id/negative_button & quot;] & # x27;) .click ();
//} catch (erroneousness) {
// console.log (& # x27; Something went wrong due to \n\n & # x27;, error, & # x27; \n\n, but it was not severe enough to let the trial fail & # x27;);
// }
// Now do all the setting magic for challenge 2 and just look until ` WEBVIEW_chrome ` is given back
driver.waitUntil (
// Check if there is a setting that holds ` WEBVIEW_chrome `
() = & gt; {
return driver.getContexts () .find (context = & gt; context === & # x27; WEBVIEW_chrome & # x27;);
},
// This time to verify if the condition is met
15000,
// The custom error content when the webview is not plant in clip
& # x27; Could not find the correct ` WEBVIEW_chrome ` -context & # x27;,
);
// Now change to the correct webview
driver.switchContext (& # x27; WEBVIEW_chrome & # x27;);
// What if there is no just once
expect (driver.getTitle ()) .toEqual (& # x27; Cross Browser Testing, Selenium Testing, Mobile Testing | Sauce Labs & # x27;);
});
});
Conclusion
I hope you establish this tech tip utile. If you ’ re look for a quick and easy way to quiz real E2E exploiter feed with your own app on Android, you can try for free with Sauce Labs. Until next time….happy examination!
Wim Selles helps solve automation challenges by day—and practices his passion for front-end examination automation at night. Wim enjoys create his own node.js modules and contributing to open rootage projects. You can find Wim on LinkedIn and Twittter @ wswebcreation.
Staff Product Manager at Sauce Labs
Topics
Share this post
Automate This With SUSA
Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts needed.
Try SUSA FreeTest Your App Autonomously
Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.
Try SUSA Free