Testing A Hybrid Mobile App Using Appium
Sauce AI for Test Authoring: Move from intent to execution in minutes.|xBack to ResourcesBlogPosted
Sauce AI for Test Authoring: Move from intent to execution in minutes.
|
x
If you want to test hybrid mobile apps—or any other kind of app, for that matter—Appium is a great pick. In this article, I provide an Appium example of testing a hybrid mobile app built with React.
Appium ’ s Versatility: Mobile Testing and Beyond
First, though, a few words on why Appium is a great option for hybrid roving app testing. Appium is an open source mechanization testing framework for use with native and hybrid mobile apps. It direct to be words and framework agnostic—meaning it can work with any lyric and prove framework of your choice.
Once you take a language, more often than not, Appium work with it. Appium can be utilize to test iOS, Android and Windows applications, still if it & # x27; s hybrid. Appium aims to automate any roving app from any language and any test fabric, with full access to backend APIs and DBs from test codification. It does this by using theWebDriver protocolwhich is a control interface that enable programs to remotely instruct the behavior of web browsers.
To show how versatile Appium is, in this article we ’ ll use it for a scenario that is somewhat off the beaten path: Testing a React Native intercrossed app using the py.test fabric.
My Hybrid Mobile App
My hybrid app is compose in ReactJS, and my test suit are written in Python. They all work together by communicating with the Appium WebDriver client.
This driver connects to the Appium service that runs in the ground on your local machine. Appium is so full-bodied that you can also choose to not run the service in your local machine! You can just connect to the Sauce Labs cloud-based mobile testing platform and leave it up to them to maintain all of the emulators, simulators and existent device you would like to try against.
Getting Started with Appium
To kick things off, let & # x27; s establish Appium.
1brew install libimobiledevice -- HEAD2brew install carthage3npm install -g appium4npm install wd5npm install -g ios-deploy6
I & # x27; m installing Appium on MacOS with ` brew ` and ` node ` already install.
Now let & # x27; s start the Appium service with theappium command.
You should now see:
[Appium] Welcome to Appium v1.6.3
[Appium] Appium REST http interface hearer started on 0.0.0.0:4723
Connecting the Puzzle
Alright. We hold Appium running. Now what?
All we have to do is write trial cases in whatever language and framework we choose and connect them to Appium. I & # x27; m opt Python and ` py.test `. You can besides choose Javascript and Mocha. But I prefer Python. I & # x27; ll be testing a React Native hybrid mobile app compiled to both iOS (.app) and Android (.apk).
The test cases instruct Appium to fill in text boxes, click on buttons, check substance on the screen, and even wait a specific amount of clip for a screen to load. If at any clip Appium is unable to find elements, it will throw an exception and your test fails.
Let & # x27; s part testing!
Creating a React Native Hybrid Mobile App
You can fetch the dummy application from myGitHub.
The app contains two text fields, one for username and one for password, and besides a button to “ log in. ” For the purposes of this article, the login function does nil, absolutely nothing!
SUSA automates exploratory testing with persona-driven behavior, catching bugs that scripted automation misses.
Writing Tests
Let & # x27; s initiative get sure to have ` pytest ` and the Python Appium Client installed. It & # x27; s as bare as:
pip install pytest
pip install Appium-Python-Client
Essentially, ` py.test ` will connect to the Appium service and launch the coating, either on the simulator or physical twist. You will receive to provide the Appium endpoint and the emplacement of the ` .app ` or ` .apk ` file. You can too delimit the device you & # x27; d like to test it on.
Creating a Base Test Class
Let & # x27; s make a base test category that handles high-level use such as connecting to the Appium service and terminating the connection.
1importunittest2from appium import webdriver3classAppiumTest(unittest.TestCase):4def setUp(self):5self.driver= webdriver.Remote(6command_executor=& # x27; http: //127.0.0.1:4723/wd/hub & # x27;,7desired_capabilities={8& # x27; app & # x27;:& # x27; PATH_OF_.APP_FILE & # x27;,9& # x27; platformName & # x27;:& # x27; iOS & # x27;,10& # x27; deviceName & # x27;:& # x27; iPhone Simulator & # x27;,11& # x27; automationName & # x27;:& # x27; XCUITest & # x27;,12})13def tearDown(self):14self.driver.quit()
Selecting Elements
How do we recount Appium to chatter on this button or filling in this form? We do this by identifying the elements either with classnames, IDs, XPaths, accessibility label, or more.
We & # x27; ll use both XPaths and availability labels here, since React Native hasnot yet implemented IDs.
Finding Elements Using XPaths
You can find an element with two of its dimension: its selector and name. For example, if your TextView called “ Welcome to Appium, ” the selector would be “ text ” and “ Welcome To Appium ” would be used as the identifier.
driver.find_element_by_xpath (& # x27; // * [@ text= & quot; Welcome To Appium & quot;] & # x27;)
This chooser looks for a DOM element that has a text attribute of “ Welcome To Appium. ”
It & # x27; s no shocker that this might not be the alone TextView element with “ Welcome To Appium. ” What happens when there are multiple element? You can so use the function ` find_elements_by_xpath `, which returns a lean of the elements that match your query.
And it & # x27; s also no shocker that these values undergo uninterrupted alteration. It would not be fun to rewrite examination for every minor modification that happens in the app. That & # x27; s where accessibility labels come in.
Finding Elements Using Accessibility Labels
A much more stable pick is to find elements apply their accessibility labels. These rarely get alter during development. However, in React Native, availability labels can only be added to View elements. The workaround is to wrap any element you will need to test in a View element.
& lt; View accessibilityLabel= & quot; Welcome To Appium & quot; & gt;
& lt; Text & gt; Welcome To Appium & lt; /Text & gt;
& lt; /View & gt;
Do note that handiness labels are read by screen readers, so make sure to name reasonably.
You can now access the ingredient like this:
driver.find_elements_by_accessibility_id (& quot; Welcome To Appium & quot;)
Transitioning Between Pages
The almost mutual thing you & # x27; ll see in examination cases on either Appium or Selenium is the immense turn of sleep argument. This is because the tryout cases you write will experience no knowledge or binding to a screen.
Say your application has a button on one page and a form on another. And this button is utilise to transition from one page to another. You will want your test example to tick on a button, transition the page, and then fill in a form. However, your test case will never know that it just transitioned between two Page! The webdriver protocol can only access elements on a page, and not a page itself. Because of this, sopor for an arbitrary amount of time is very common when you expect a page to transition. However, this is very rudimentary. With Appium, we can alternatively instruct it to & # x27; wait_until & # x27; an element is on a page.
Finally, Let ’ s Write Some Tests!
You can find all the tryout cases on myGitHub repo.
Here’s how it looksfor an iOS React Native app:
1import os2import unittest3import time4from appium import webdriver5import xml6classAppiumTest(unittest.TestCase):7defsetUp(self):8self.driver = webdriver.Remote(9command_executor=& # x27; http: //127.0.0.1:4723/wd/hub & # x27;,10desired_capabilities={11& # x27; app & # x27;: os.path.abspath(APP_PATH),12& # x27; platformName & # x27;:& # x27; iOS & # x27;,13& # x27; deviceName & # x27;:& # x27; iPhone Simulator & # x27;,14& # x27; automationName & # x27;:& # x27; XCUITest & # x27;,15})16deftearDown(self):17self.driver.quit()18defrepl(self):19import pdb; pdb.set_trace()20defdump_page(self):21withopen(& # x27; appium_page.xml & # x27;,& # x27; w & # x27;)as f:22raw = self.driver.page_source23ifnot raw:24return25source = xml.dom.minidom.parseString(raw.encode(& # x27; utf8 & # x27;))26f.write(source.toprettyxml())27def_get(self, text, index=None, partial=False):28selector =& quot; name & quot;29if text.startswith(& # x27; # & # x27;):30elements = self.driver.find_elements_by_accessibility_id(text[1:])31elif partial:32elements = self.driver.find_elements_by_xpath(& # x27; // * [contains (@ % s, & quot; % s & quot;)] & # x27;%(selector, text))33else:34elements = self.driver.find_elements_by_xpath(& # x27; // * [@ % s= & quot; % s & quot;] & # x27;%(selector, text))35ifnot elements:36raise Exception()37if index:38return elements[index]39if index isNoneandlen(elements)>1:40raiseIndexError(& # x27; More that one element found for % r & # x27;% text)41return elements[0]42defget(self, text,*args,**kwargs):43& # x27; & # x27; & # x27; try to get for X seconds; composition over loading waits/sleeps & # x27; & # x27; & # x27;44timeout_seconds= kwargs.get(& # x27; timeout_seconds & # x27;,10)45start = time.time()46while time.time()- start <timeout_seconds:47try:48return self._get(text,*args,**kwargs)49exceptIndexError:50raise51except:52pass53# self.wait (.2)54time.sleep(.2)55raise Exception(& # x27; Could not find text % r after % r seconds & # x27;%(56text,timeout_seconds))57defwait_until(self,*args,**kwargs):58# only care if there is at least one match59return self.get(*args, index=0,**kwargs)60classExampleTests(AppiumTest):61deftest_loginError(self):62self.dump_page()63self.wait_until(& # x27; Login & # x27;, partial=True)64self.get(& # x27; Please enter your e-mail & # x27;).send_keys(& # x27; foo @ example.com\n & # x27;)65self.get(& # x27; Please enter your password & # x27;).send_keys(& # x27; Password1 & # x27;)66self.driver.hide_keyboard()67self.get(& # x27; Press me to submit & # x27;, index=1).click()68self.wait_until(& # x27; Please check your credentials & # x27;)69assertTrue70deftest_loginSuccess(self):71self.dump_page()72self.wait_until(& # x27; Login & # x27;, partial=True)73self.get(& # x27; Please enter your email & # x27;).send_keys(& # x27; dummyemail @ example.com\n & # x27;)74self.get(& # x27; Please inscribe your password & # x27;).send_keys(& # x27; 121212 & # x27;)75self.driver.hide_keyboard()76self.get(& # x27; Press me to submit & # x27;, index=1).click()77self.wait_until(& # x27; Login Successful & # x27;)78assertTrue79deftest_loginEmptyEmail(self):80self.dump_page()81self.wait_until(& # x27; Login & # x27;, partial=True)82self.get(& # x27; Please enter your email & # x27;).send_keys(& # x27; \n & # x27;)83self.get(& # x27; Please enter your password & # x27;).send_keys(& # x27; 121212 & # x27;)84self.driver.hide_keyboard()85self.get(& # x27; Press me to subject & # x27;, index=1).click()86self.wait_until(& # x27; Please enter your e-mail ID & # x27;)87assertTrue88deftest_loginEmptyPassword(self):89self.dump_page()90self.wait_until(& # x27; Login & # x27;, partial=True)91self.get(& # x27; Please enter your email & # x27;).send_keys(& # x27; dummyemail @ example.com\n & # x27;)92self.get(& # x27; Please enrol your password & # x27;).send_keys(& # x27; & # x27;)93self.driver.hide_keyboard()94self.get(& # x27; Press me to submit & # x27;, index=1).click()95self.wait_until(& # x27; Please enter your password & # x27;)96assertTrue97
And here ’ s how it seem for an Android React Native app:
1import os2import unittest3import time4from appium import webdriver5import xml6classAppiumTest(unittest.TestCase):7defsetUp(self):8abs_path = os.path.abspath(APK_PATH)9self.driver = webdriver.Remote(10command_executor=& # x27; http: //127.0.0.1:4723/wd/hub & # x27;,11desired_capabilities={12& # x27; app & # x27;: os.path.abspath(abs_path),13& # x27; platformName & # x27;:& # x27; Android & # x27;,14& # x27; deviceName & # x27;:& # x27; Nexus 6P API 25 & # x27;,15})16deftearDown(self):17self.driver.quit()18defrepl(self):19import pdb; pdb.set_trace()20defdump_page(self):21withopen(& # x27; appium_page.xml & # x27;,& # x27; w & # x27;)as f:22raw = self.driver.page_source23ifnot raw:24return25source = xml.dom.minidom.parseString(raw.encode(& # x27; utf8 & # x27;))26f.write(source.toprettyxml())27def_get(self, text, index=None, partial=False):28selector =& quot; content-desc & quot;29if text.startswith(& # x27; # & # x27;):30elements = self.driver.find_elements_by_accessibility_id(text[0:])31elif partial:32elements = self.driver.find_elements_by_xpath(& # x27; // * [contains (@ % s, & quot; % s & quot;)] & # x27;%(selector, text))33else:34elements = self.driver.find_elements_by_xpath(& # x27; // * [@ % s= & quot; % s & quot;] & # x27;%(selector, text))35ifnot elements:36raise Exception()37if index:38return elements[index]39if index isNoneandlen(elements)>1:40raiseIndexError(& # x27; More that one element found for % r & # x27;% text)41return elements[0]42defget(self, text,*args,**kwargs):43timeout_seconds= kwargs.get(& # x27; timeout_seconds & # x27;,10)44start = time.time()45while time.time()- start <timeout_seconds:46try:47return self._get(text,*args,**kwargs)48exceptIndexError:49raise50except:51pass52# self.wait (.2)53time.sleep(.2)54raise Exception(& # x27; Could not find text % r after % r seconds & # x27;%(55text,timeout_seconds))56defwait_until(self,*args,**kwargs):57# only caution if there is at least one match58return self.get(*args, index=0,**kwargs)59classExampleTests(AppiumTest):60deftest_loginError(self):61time.sleep(5)62self.dump_page()63self.wait_until(& # x27; Please enter your email & # x27;, partial=False)64self.get(& # x27; Please recruit your e-mail & # x27;).send_keys(& # x27; foo @ example.com\n & # x27;)65self.get(& # x27; Please enter your password & # x27;).send_keys(& # x27; Password1 & # x27;)66self.driver.hide_keyboard()67self.get(& # x27; Press me to state & # x27;, index=0).click()68self.wait_until(& # x27; Please ascertain your credentials & # x27;)69assertTrue70deftest_loginSuccess(self):71time.sleep(5)72self.dump_page()73self.wait_until(& # x27; Please enter your email & # x27;, partial=False)74self.get(& # x27; Please participate your email & # x27;).send_keys(& # x27; dummyemail @ example.com\n & # x27;)75self.get(& # x27; Please enter your password & # x27;).send_keys(& # x27; 121212 & # x27;)76self.driver.hide_keyboard()77self.get(& # x27; Press me to submit & # x27;, index=0).click()78self.wait_until(& # x27; Login Successful & # x27;)79assertTrue80deftest_loginEmptyEmail(self):81time.sleep(5)82self.dump_page()83self.wait_until(& # x27; Please enter your e-mail & # x27;, partial=False)84self.get(& # x27; Please inscribe your email & # x27;).send_keys(& # x27; \n & # x27;)85self.get(& # x27; Please enter your countersign & # x27;).send_keys(& # x27; 121212 & # x27;)86self.driver.hide_keyboard()87self.get(& # x27; Press me to state & # x27;)88
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