How to Automate the Zoom Video Meetings App
Tags: appium, digitaltestautomat, mobileapps, voiceconferencing
Published: 2020-04-27 05:22:01

---
title: How to Automate the Zoom Video Meetings App
published: true
date: 2020-04-23 17:11:42 UTC
tags: Appium,digital test automat,mobile apps,Voice Conferencing
canonical_url: https://www.headspin.io/blog/appium/how-to-automate-the-zoom-video-meetings-app/
---
<p>Last week I tried something new: <a href="https://ui.headspin.io/university/feed/automation-happy-hour-1">Automation Happy Hour</a>. 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!</p>
<p>(NB: In the live stream, I chose to work in Python, but for this article I converted all the code examples to Java.)</p>
<h3 id="starting-a-session-with-zoom">Starting a session with Zoom</h3>
<p><a href="https://zoom.us/">Zoom</a> 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?</p>
<p>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 <code>app</code> capability.</p>
<p>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 <code>adb dumpsys window windows</code>, I determined Zoom’s info, and entered that into my set of capabilities:</p>
</div>
				</div>
				</div>
				<div data-id="ed6bc29" data-element_type="widget" data-widget_type="html.default">
				<div>
			<pre style="background:#000;color:#f8f8f8">caps.setCapability(<span style="color:#65b042">"platformName"</span>, <span style="color:#65b042">"Android"</span>)<span style="color:#e28964">;</span>
caps.setCapability(<span style="color:#65b042">"deviceName"</span>, <span style="color:#65b042">"Android"</span>)<span style="color:#e28964">;</span>
caps.setCapability(<span style="color:#65b042">"appPackage"</span>, <span style="color:#65b042">"us.zoom.videomeetings"</span>)<span style="color:#e28964">;</span>
caps.setCapability(<span style="color:#65b042">"appActivity"</span>, <span style="color:#65b042">"com.zipow.videobox.LauncherActivity"</span>)<span style="color:#e28964">;</span>
caps.setCapability(<span style="color:#65b042">"automationName"</span>, <span style="color:#65b042">"UiAutomator2"</span>)<span style="color:#e28964">;</span>
</pre> </div>
				</div>
				<div data-id="140eae2" data-element_type="widget" data-widget_type="text-editor.default">
				<div>
					<div>
<p>(The important bits above are the <code>appPackage</code> and <code>appActivity</code> capabilities). Using these caps, we can launch zoom in an automation context!</p>
<h3 id="joining-a-meeting">Joining a meeting</h3>
<p>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:</p>
</div>
				</div>
				</div>
				<div data-id="2787a9b" data-element_type="widget" data-widget_type="html.default">
				<div>
			<pre style="background:#000;color:#f8f8f8"><span style="color:#e28964">private</span> <span style="color:#e28964">static</span> final <span style="color:#99cf50">String</span> MEETING_ID <span style="color:#e28964">=</span> <span style="color:#9b859d">System</span>.getenv(<span style="color:#65b042">"ZOOM_MEETING_ID"</span>);
<span style="color:#e28964">private</span> <span style="color:#e28964">static</span> final <span style="color:#99cf50">String</span> MEETING_PW <span style="color:#e28964">=</span> <span style="color:#9b859d">System</span>.getenv(<span style="color:#65b042">"ZOOM_MEETING_PW"</span>);
</pre> </div>
				</div>
				<div data-id="5a6984d" data-element_type="widget" data-widget_type="text-editor.default">
				<div>
					<div>
<p>(I.e., you need to set the <code>ZOOM_MEETING_ID</code> and <code>ZOOM_MEETING_PW</code> environment vars to run the examples successfully, based on a meeting that you own or know about–and, please, no zoom-bombing!)</p>
<p>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).</p>
<p>To figure out how to find all the elements I needed to interact with, I used <a href="https://github.com/appium/appium-desktop">Appium Desktop</a> 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:</p>
</div>
				</div>
				</div>
				<div data-id="9bfd63c" data-element_type="widget" data-widget_type="html.default">
				<div>
			<pre style="background:#000;color:#f8f8f8"><span style="color:#e28964">private</span> By JOIN_MEETING_BUTTON <span style="color:#e28964">=</span> By.id(<span style="color:#65b042">"btnJoinConf"</span>);
<span style="color:#e28964">private</span> By MEETING_ID_FIELD <span style="color:#e28964">=</span> By.id(<span style="color:#65b042">"edtConfNumber"</span>);
<span style="color:#e28964">private</span> By ACTUALLY_JOIN_MEETING_BUTTON <span style="color:#e28964">=</span> By.id(<span style="color:#65b042">"btnJoin"</span>);
<span style="color:#e28964">private</span> By PASSWORD_FIELD <span style="color:#e28964">=</span> By.id(<span style="color:#65b042">"edtPassword"</span>);
<span style="color:#e28964">private</span> By PASSWORD_OK_BUTTON <span style="color:#e28964">=</span> By.id(<span style="color:#65b042">"button1"</span>);
<span style="color:#e28964">private</span> By LEAVE_BTN <span style="color:#e28964">=</span> By.id(<span style="color:#65b042">"btnLeave"</span>);
</pre> </div>
				</div>
				<div data-id="0da6fd9" data-element_type="widget" data-widget_type="text-editor.default">
				<div>
					<div>
<p>You can see that, apart from <code>PASSWORD_OK_BUTTON</code> (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!</p>
<p>Using these locators, we can very straightforwardly walk through the join meeting flow, as the video below shows:</p>
</div>
				</div>
				</div>
				<div data-id="bed89e5" data-element_type="widget" data-widget_type="image.default">
				<div>
					<div>
										<img width="399" height="719" src="https://www.headspin.io/wp-content/uploads/2020/04/zoom.gif" alt=""> </div>
				</div>
				</div>
				<div data-id="44c6bbf" data-element_type="widget" data-widget_type="text-editor.default">
				<div>
					<div><p>The test code that implements this is as follows (using the locators defined above):</p></div>
				</div>
				</div>
				<div data-id="14d8e11" data-element_type="widget" data-widget_type="html.default">
				<div>
			<pre style="background:#000;color:#f8f8f8">@<span style="color:#3387cc">Test</span>
public void testJoinMeeting<span style="color:#3387cc">()</span> {
<span style="color:#e28964">//</span> navigate through the <span style="color:#3387cc">UI</span> to join a meeting with correct meeting <span style="color:#dad085">id</span> <span style="color:#dad085">and</span> password
waitFor(<span style="color:#3387cc">JOIN_MEETING_BUTTON</span>)<span style="color:#e28964">.</span>click<span style="color:#3387cc">()</span>;
waitFor(<span style="color:#3387cc">MEETING_ID_FIELD</span>)<span style="color:#e28964">.</span>sendKeys(<span style="color:#3387cc">MEETING_ID</span>);
driver<span style="color:#e28964">.</span>findElement(<span style="color:#3387cc">ACTUALLY_JOIN_MEETING_BUTTON</span>)<span style="color:#e28964">.</span>click<span style="color:#3387cc">()</span>;
waitFor(<span style="color:#3387cc">PASSWORD_FIELD</span>)<span style="color:#e28964">.</span>sendKeys(<span style="color:#3387cc">MEETING_PW</span>);
driver<span style="color:#e28964">.</span>findElement(<span style="color:#3387cc">PASSWORD_OK_BUTTON</span>)<span style="color:#e28964">.</span>click<span style="color:#3387cc">()</span>;

<span style="color:#e28964">//</span> prove that we made it into the meeting by finding the 'leave' button
waitFor(<span style="color:#3387cc">LEAVE_BTN</span>);
}
</pre> </div>
				</div>
				<div data-id="6e2cdc7" data-element_type="widget" data-widget_type="text-editor.default">
				<div>
					<div><p>Here we are using a special <code>waitFor</code> helper which just reduces verbosity in the code:</p></div>
				</div>
				</div>
				<div data-id="e53eab3" data-element_type="widget" data-widget_type="html.default">
				<div>
			<pre style="background:#000;color:#f8f8f8">private WebElement waitFor(By locator) {
<span style="color:#e28964">return</span> <span style="color:#e28964">wait</span>.<span style="color:#e28964">until</span>(ExpectedConditions.presenceOfElementLocated(locator));
}
</pre> </div>
				</div>
				<div data-id="e2feded" data-element_type="widget" data-widget_type="text-editor.default">
				<div>
					<div>
<p>(You might have observed that sometimes we use <code>waitFor</code> and sometimes we just use <code>driver.findElement</code>. Why is that? It wouldn’t hurt to use <code>waitFor</code> 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 <em>not</em> 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!)</p>
<p>So that’s it! You can as always check out the <a href="https://github.com/cloudgrey-io/appiumpro/blob/master/java/src/test/java/Edition113_Automating_Zoom.java">full code example</a> 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 <a href="https://twitter.com/jlipps">follow me on Twitter</a>, where I announce these things as well as run polls on what apps I should automate in future episodes.</p>
<p>Oh, and don’t forget to <a href="https://appiumpro.com/contact">let me know</a> if you come up with any interesting (and non-nefarious) use cases for automating Zoom specifically!</p>
</div>
				</div>
				</div>
						</div>
			</div>
		</div>
						</div>
			</div>
		</section>

The post [How to Automate the Zoom Video Meetings App](https://www.headspin.io/blog/appium/how-to-automate-the-zoom-video-meetings-app/) appeared first on [HeadSpin](https://www.headspin.io).