This article is the sixth in a multi-part series on test speed and reliability, inspired by a webinar I gave on the same subject (you can watch the webinar here). You might also want to check out the previous episodes on Test Flakiness, Finding Elements Reliably, Waiting for App States, Dealing with Unfindable Elements, and Setting Up App State.
Appium sessions are not just Appium sessions. Or at least, they don't have to be. When you start a session, you can tweak the way Appium operates by means of session initialization parameters known as "desired capabilities" (in Selenium parlance; in the language of the W3C WebDriver spec they are simply "capabilities"). We all know about capabilities (which we all love to abbreviate as "caps") because we have to get dirty with them just to start a test successfully, using capabilities like platformName or deviceName to let Appium know what kind of thing we're interested in automating. Appium actually supports a host of other capabilities---over 100 of them, actually.
All these optional caps basically tell Appium to behave differently. There are caps to tweak default timeouts, to set the language on a device before the session, to adjust the orientation, or work with a host of system-specific issues like paths to important binaries or which ports Appium should use for its internal mechanisms. Some of these capabilities are useful from the perspective of speed or reliability, and can help stabilize tests, especially if you find yourself in the specific scenario that they were designed to address. So, let's take a look!
By default, Appium runs a reset process after each session, to ensure that a device and app are as fresh and clean as when they first arrived. This of course is important for ensuring determinism in your test suite: you don't want the aftereffects of one test to change the operation of a subsequent test! If your tests don't require this kind of clean state, and you're absolutely sure of it, you can set this capability to true to avoid some of the time-consuming cleaning subroutines. If you don't need the safety, take a shortcut for the sake of speed!
The opposite of the previous cap is fullReset, which does even more cleanup than by default. Set this cap to true to add even more time-consuming cleaning subroutines. This might be necessary if the default cleanup is not enough to make your build reliable, and you know that the instability is due to leftover state from previous tests.
Both iOS and Android have the concept of 'headless' virtual devices, and Appium can still work with devices that are running in this mode. Set this cap to true to launch a simulator or emulator in headless mode, potentially leading to a performance boost, especially if you're running in a virtualized or resource-constrained environment (such as a VM farm). Of course, in headless mode any tooling you have around taking videos or screenshots outside of Appium will not work.
The only way to check for toast messages on Android is for the Appium UiAutomator2 driver to run a loop constantly checking the state of the device. Running a loop like this takes up valuable CPU cycles and has been observed to make scrolling less consistent, for example. If you don't need the features that require the watcher loop (like toast verification), then set this cap to true to turn it off entirely and save your device some cycles.
Set to true to have Appium attempt to automatically determine your app permissions and grant them, for example to avoid system popups asking for permission later on in the test.
Appium doesn't assume that your device is unlocked, and it should be to successfully run tests. So it installs and runs a little helper app that tries to unlock the screen before a test. Sometimes this works, and sometimes this doesn't. But that's beside the point: either way, it takes time! If you know your screen is unlocked, because you're managing screen state with something other than Appium, tell Appium not to bother with this little startup routine and save yourself a second or three, by setting this cap to true.
appWaitPackage and appWaitActivity
Android activities can be kind of funny. In many apps, the activity used to launch the app is not the same as the activity which is active when the user initially interacts with the application. Typically it's this latter activity you care about when you run an Appium test. You want to make sure that Appium doesn't consider the session started until this activity is active, regardless of what happened to the launch activity.
In this scenario, you need to tell Appium to wait for the correct activity, since the one it automatically retrieves from your app manifest will be the launch activity. You can use the appWaitPackage and appWaitActivity to tell Appium to consider a session started (and hence return control to your test code) only when the package and activity specified have become active. This can greatly help the stability of session start, because your test code can assume your app is on the activity expects when the session starts.
Android has two modes for expressing its layout hierarchy: normal and "compressed". The compressed layout hierarchy is a subset of the hierarchy that the OS itself sees, restricted to elements which the OS thinks are more relevant for users, for example elements with accessibility information set on them. Because compressed mode generates a smaller XML file, and perhaps for other Android-internal reasons, it's often faster to get the hierarchy in compressed mode. If you're running into page source queries taking a very long time, you might try setting this cap to true.
Note that the XML returned in the different modes is ... different. Which means that XPath queries that worked in one mode will likely not work in the other. Make sure you don't change this back and forth if you rely on XPath!
usePrebuiltWDA and derivedDataPath
Typically, Appium uses xcodebuild under the hood to both build WebDriverAgent and kick off the XCUITest process that powers the test session. If you have a prebuilt WebDriverAgent binary and would like to save some time on startup, set the usePrebuiltWDA cap to true. This cap could be used in conjunction with derivedDataPath, which is the path to the derived data folder where your WebDriverAgent binary is dumped by Xcode.
For large applications, it can be faster for Appium to deal with the app hierarchy internally as JSON, rather than XML, and convert it to XML at the "edge", so to speak---in the Appium driver itself, rather than lower in the stack. Basically, give this a try if getting the iOS app source is taking forever.
Sometimes, large iOS applications can take a while to launch, but there's no way for Appium to automatically detect when an app is ready for use or not. If you have such an app, set this cap to the number of milliseconds you'd like Appium to wait after WebDriverAgent thinks the app is online, before Appium hands back control to your test script. It might help make session startup a bit more stable.
If you notice errors during typing, for example the wrong keys being pressed or visual oddities you notice while watching a test, try slowing the typing down. Set this cap to an integer and play around with the value until things work. Lower is slower, higher is faster! The default is 60.
Appium has its own methods for capturing screenshots from simulators and devices, but especially on real devices this can be slow and/or flaky. If you're a fan of the libimobiledevice suite and happen to have idevicescreenshot on your system, you can use this cap to let Appium know you'd prefer to retrieve the screenshot via a call to that binary instead of using its own internal methods. To make it happen, simply set this cap to the string "idevicescreenshot"!
Element visibility checks in XCUITest are fraught with flakiness and complexity. By default, the visibility checks available don't always do a great job. Appium implemented another type of visibility check inside of WebDriverAgent that might be more reliable for your app, though it comes with the downside that the checks could take longer for some apps. As with many things in life, we sometimes have to make trade-offs between speed and reliability.
Well, I hope you found something in the list above that was intriguing, or that perhaps you didn't already know about. Don't forget to check the Appium docs periodically, as many features or workarounds are made available through the ever-growing set of available capabilities!