How to Automate the Zoom Video Meetings App

[This post originally appeared on appiumpro.com

Last week I tried something new: Automation Happy Hour. In this live stream event, I downloaded the Zoom app for Android and tried to build a simple testsuite for it in about an hour. I think this type of real-world “case study” is really interesting. My expectation is that discussing automation of real apps “in the wild” would also be useful for Appium Pro readers, so periodically, I will take the results of these explorations and turn them into Appium Pro articles!

(NB: In the live stream, I chose to work in Python, but for this article I converted all the code examples to Java.)

Starting a session with Zoom

Zoom is incredibly popular. It seems like everyone is using it for their video meetings these days. How can we launch it in an automation context with Appium?

First, we need to decide whether to have Appium install the Zoom app for us, or to have the app on the phone and simply launch it when our session starts. I already had the Zoom app on my device, so I chose the latter route, but you could also find a downloadable Zoom APK, put it somewhere on your system, and reference it as the app capability.

Since I had the app already, I needed to know its package id and launching activity to get Appium to start it for me. Using the helpful ADB command adb dumpsys window windows, I determined Zoom’s info, and entered that into my set of capabilities:

caps.setCapability("platformName", "Android");
caps.setCapability("deviceName", "Android");
caps.setCapability("appPackage", "us.zoom.videomeetings");
caps.setCapability("appActivity", "com.zipow.videobox.LauncherActivity");
caps.setCapability("automationName", "UiAutomator2");

(The important bits above are the appPackage and appActivity capabilities). Using these caps, we can launch zoom in an automation context!

Joining a meeting

The goal is to do something useful with automation, so let’s see how it’s possible to join a Zoom meeting from a device controlled by Appium. Before we begin, we’ll obviously need a Zoom meeting to join! I started one on my computer, so that I could determine its meeting ID and password. These are the two pieces of information you’ll need that are unique to your case, and to work with my code examples, you’ll need to set them as environment variables, so that they can be read from our test class:

private static final String MEETING_ID = System.getenv("ZOOM_MEETING_ID");
private static final String MEETING_PW = System.getenv("ZOOM_MEETING_PW");

(I.e., you need to set the ZOOM_MEETING_ID and ZOOM_MEETING_PW environment vars to run the examples successfully, based on a meeting that you own or know about–and, please, no zoom-bombing!)

It turns out that Zoom is a highly automatable app! Its developers included resource IDs for all the useful elements. There were no content descriptions (accessibility IDs) that I could find, however, which makes me think the app may not be super accessible (or, it relies on the default accessibility strategies based on text labels).

To figure out how to find all the elements I needed to interact with, I used Appium Desktop to inspect the app (after filling out all the appropriate caps, and starting a session with the inspector). It turns out that the Zoom elements follow a pretty standard naming scheme. Here’s the part of our test class where we define the locators, for example:

private By JOIN_MEETING_BUTTON = By.id("btnJoinConf");
private By MEETING_ID_FIELD = By.id("edtConfNumber");
private By ACTUALLY_JOIN_MEETING_BUTTON = By.id("btnJoin");
private By PASSWORD_FIELD = By.id("edtPassword");
private By PASSWORD_OK_BUTTON = By.id("button1");
private By LEAVE_BTN = By.id("btnLeave");

You can see that, apart from PASSWORD_OK_BUTTON (which is referring to a system alert element), every UI element has a camelCase ID with an abbreviation of the element type at front. Pretty respectable, Zoom!

Using these locators, we can very straightforwardly walk through the join meeting flow, as the video below shows:

The test code that implements this is as follows (using the locators defined above):

@Test
public void testJoinMeeting() {
// navigate through the UI to join a meeting with correct meeting id and password
waitFor(JOIN_MEETING_BUTTON).click();
waitFor(MEETING_ID_FIELD).sendKeys(MEETING_ID);
driver.findElement(ACTUALLY_JOIN_MEETING_BUTTON).click();
waitFor(PASSWORD_FIELD).sendKeys(MEETING_PW);
driver.findElement(PASSWORD_OK_BUTTON).click();

// prove that we made it into the meeting by finding the 'leave' button
waitFor(LEAVE_BTN);
}

Here we are using a special waitFor helper which just reduces verbosity in the code:

private WebElement waitFor(By locator) {
return wait.until(ExpectedConditions.presenceOfElementLocated(locator));
}

(You might have observed that sometimes we use waitFor and sometimes we just use driver.findElement. Why is that? It wouldn’t hurt to use waitFor everywhere, but in some cases we know that an element will already be present, because we’ve already waited for another element on the same view. If that first element is not actually present immediately, then there’s no reason to wait however many seconds for it to become present. The test is doomed and we might as well fail quickly!)

So that’s it! You can as always check out the full code example as well. And if you’re interested in checking out some of the Automation Happy Hour live streams (or the video recordings of them), don’t forget to follow me on Twitter, where I announce these things as well as run polls on what apps I should automate in future episodes.

Oh, and don’t forget to let me know if you come up with any interesting (and non-nefarious) use cases for automating Zoom specifically!