You can write one yourself using Appium. Appium is an open-source tool for automating native, mobile web, and hybrid applications on iOS and Android platforms. It has an easy learning curve, owing to the fact that at its core, it implements a client/server architecture; and exposes a REST API for your Appium client to communicate with your Appium server. At an abstract level; this is how Appium works:
Here's a little background on the WebDriver protocol, and Appium's design stance on it:
WebDriver (aka "Selenium WebDriver") specifies a client-server protocol (known as the JSON Wire Protocol). Given this client-server architecture, a client written in any language can be used to send the appropriate HTTP requests to the server. ... WebDriver has become the de facto standard for automating web browsers, and is a W3C Working Draft. Why do something totally different for mobile? Instead we have extended the protocol with extra API methods useful for mobile automation.
Desired capabilities are a set of keys and values (i.e., a map or hash) sent to the Appium server to tell the server what kind of automation session we're interested in starting up. There are also various capabilities which can modify the behavior of the server during automation. For example, we might set the
platformNamecapability toiOSto tell Appium that we want an iOS session, rather than an Android one.
bootstrap.jar in the case of Android; bootstrap.js in the case of iOS) to perform the commands/actions sent by the client, on the concerned app.Following images (taken from Google Images), summarize it all quite well.
Following is trivial example code, demonstrating the above concepts; which I have written long back to do test automation for a sample shopping list app. The server is run locally, but you can see from the commented code underneath #TestObject Config., we can also choose to run our server using a third party Appium provider. The function SAMPLE_LIST parses a wishlist page; gets the names of the corresponding items; the test script then populates these items in the concerned app.
#!/usr/bin/env python
import os
import unittest
import requests
from appium import webdriver
from bs4 import BeautifulSoup
PATH = lambda p: os.path.abspath\
(os.path.join(os.path.dirname(__file__), p))
LIST_NAME = "Steam Games Wishlist"
def SAMPLE_LIST():
'''
Returns a list of items,
parsed from an arbitrarily selected wishlist on the web.
'''
wishlist_url = "steamcommunity.com/id/brick_pandora/wishlist"
source = requests.get(wishlist_url).text
soup = BeautifulSoup(source)
wishlist = [game.text for game in soup.find_all('h4', 'ellipsis')]
return wishlist
class ShoppingListAppTests(unittest.TestCase):
def setUp(self):
desired_caps = {}
desired_caps["appPackage"] = "org.openintents.shopping"
# Local Config.
desired_caps["platformName"] = "Android"
desired_caps["platformVersion"] = "6.0"
desired_caps["deviceName"] = "Android_Emulator"
desired_caps["app"] = PATH("OI Shopping List_1.7.0.5.apk")
webdriver_url = "localhost/wd/hub"
# TestObject Config.
# desired_caps["testobject_api_key"] = "ONLY_IF_YOU_ASK_NICELY"
# desired_caps["testobject_app_id"] = "2"
# desired_caps["testobject_device"] = "LG_Nexus_4_E960_real"
# webdriver_url = "app.testobject.com/api/appium/wd/hub"
self.driver = webdriver.Remote(webdriver_url, desired_caps)
def tearDown(self):
self.driver.quit()
def test_create_new_list(self):
options_button = self.driver.find_elements_by_class_name\
("android.widget.ImageButton")[0]
options_button.click()
new_list_button = self.driver.find_elements_by_id\
("android:id/title")[0]
new_list_button.click()
new_list_textbox = self.driver.find_element_by_id\
("org.openintents.shopping:id/edittext")
new_list_textbox.send_keys(LIST_NAME)
okay_button = self.driver.find_element_by_id\
("android:id/button1")
okay_button.click()
new_list_name = self.driver.find_element_by_id\
("android:id/text1").text
self.assertEqual(new_list_name, LIST_NAME)
def test_add_items(self):
items = SAMPLE_LIST()[4:10]
# The list 'items', at index 3, has a string which contains...
# ...a unicode character for the trademark symbol, and my guess is...
# ...that the WebDriver is unable to enter it through a normal keyboard
# Removing this item, so that the testsuite runs without termination.
#items.pop(3)
text_field = self.driver.find_element_by_id\
("org.openintents.shopping:id/autocomplete_add_item")
add_button = self.driver.find_element_by_id\
("org.openintents.shopping:id/button_add_item")
# Add items
for idx, item in enumerate(items):
text_field.send_keys(item)
add_button.click()
# Very Intersting! The items with this id,
# that are not in view, are not included...
added_items = self.driver.find_elements_by_id\
("org.openintents.shopping:id/name")
#self.assertEqual(set(items),
# set([added_item.text for added_item in added_items]))
# Assert all added items in view, are in the original list
for added_item in added_items:
self.assertEqual(True, added_item.text in items)
# TODO: To get all the added items by changing the view through...
# ...some scroll action.
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(ShoppingListAppTests)
unittest.TextTestRunner(verbosity=2).run(suite)