Introduction to Uiautomator2
uiautomator2 Is a library for UI automation of Android devices using Python. The underlying layer is based on Google uiautomator, which provides a library of uiautomators that can take and manipulate any control property of any APP on the screen
Environment Setup
-
Install JDK, see This article
-
Install Android SDK, build tool version needs to be greater than 24, download and install toolkit note version, SDK configuration refer to This article
-
Install uiautomator2
pip install uiautomator2 # Install uiautomator2 uiautomator2 version # View Version uiautomator2 --help # view help
-
Install a viewer for element positioning assistance
pip install weditor # Install weditor weditor -v # View Version weditor --help # view help
Start the viewer, type weditor at the command line, or create weditor desktop shortcut weditor --shortcut to run the program from the desktop icon
Connect device
The first time you connect to the device, [ATX] and [com.github.uiautomator.test] are installed.
-
Mobile phone on [developer mode], switch on [USB debugging mode] data cable to connect to mobile phone, select [Transfer files]
-
cmd to the command line page, adb devices to see if the device is connected successfully, adb-related commands to see This article
-
When the device is connected successfully, open the viewer, click on Connect on the viewer page, a green leaf appears to indicate the connection is successful, and a mobile phone projection screen appears on the left (mobile phone is bright)
-
Connect your phone through a Python script:
import uiautomator2 as u2 # Import the uiautomator2 library and rename it u2 driver = u2.connect() # Connect mobile phone, if only one mobile phone is connected to the computer, no device information is required print(driver.info) # Print Device Information
Enter the following system information to indicate that the connection to your mobile phone is successful and you can start using uiautomator2 Library
{'currentPackageName': 'com.oppo.launcher', 'displayHeight': 2297, 'displayRotation': 0, 'displaySizeDpX': 360, 'displaySizeDpY': 800, 'displayWidth': 1080, 'productName': 'OnePlus9R_CH', 'screenOn': True, 'sdkInt': 30, 'naturalOrientation': True}
driver = u2.connect("48fd6742") # If your computer is connected to more than one mobile phone, you need to add a device serial number
Common operations
driver = u2.connect("48fd6742") # Connect device driver.screen_on() # Light up the screen driver.screen_off() # Screen off driver.unlock() # Unlock, machine test found that password entry page could not be located, and some APP s could not synchronize login page to cause the situation can not be located print(driver.app_info("com.sankuai.meituan")) # Get information about the specified APP print(driver.app_list_running())# List all running APP s print(driver.app_current()) # Get the currently open APP information print(driver.window_size()) # Get screen size print(driver.device_info) # Get detailed device information print(driver.serial) # Get device serial number print(driver.wlan_ip) # Get device IP address driver.press("back") # Click on the return key driver.open_notification() # Open notification bar message page, close notification bar with slideshow driver.open_quick_settings() # Open Quick Settings in Notification Bar driver.info.get("screen_on") # Gets if the current screen is on driver.swipe(552,2066,552,700) # Slide the screen in absolute coordinates for a default sliding time of 0.5s driver.swipe_ext("up",scale=0.5)# Slide the screen in direction, setting the sliding distance to 50% of the screen width, defaulting to 90% driver.open_url("https://www.baidu.com ") #Call the default browser directly and visit the specified website driver(description="information").click() # Click SMS APP driver.double_click(0.375, 0.496) # Double-click the specified relative coordinates driver(description="information").long_click(1) # Long press SMS APP, default long press 0.5s driver(description="Short message").send_keys("12")# Locate elements and enter text driver(description="Short message").set_text("A12")# Also locate elements and enter text driver(description="Short message").clear_text() # Empty the entered information driver(resourceId="com.sankuai.meituan:id/passport_mobile_phone").get_text() # Get Text Content driver(text="Meituan").drag_to(0.375, 0.375,duration=1) # Drag Meituan APP to specified location for 1s, default 0.5s driver.screenshot(r"D:\Download\test.png") # Save screenshots to specified location driver.push(r"C:\Download\test.png","/sdcard/") # Push screenshots to your mobile phone driver.pull("/sdcard/rider.txt","rider.txt") # Transfer files from your mobile phone to your computer driver.app_icon("com.sankuai.meituan").save(r"D:\Download\icon.png") # Get the APP icon and save it to the specified location driver.app_start("com.sankuai.meituan",stop=True) # Specify the package name to start the APP and end the application run state before starting driver.implicitly_wait(10) # Native operation, implicit wait, globally valid driver(description="Tien Tie Red Bag").exists() # Determines whether an element appears, returns True, or returns False driver(description="Film/show").wait(timeout=5) # Wait for element to appear, timeout is 5s driver(description="Running Legs").wait_gone(timeout=5) # Wait for element to disappear, timeout time is 5s driver.app_wait("com.sankuai.meituan",timeout=30,front=True) # Wait for program to start foreground running, default timeout 20s driver.wait_activity("com.meituan.mmp.lib.mp.MPActivity0",timeout=5) # Wait for active page loading to complete, default timeout of 10s driver.app_install(r"D:\Download\meituan.apk") # Install using a local installation package driver.app_install("http://www.meituan.com/mobile/download/meituan/android/meituan?from=new ") #Download and install online driver.app_uninstall("com.sankuai.meituan") # Unload APP driver.app_stop("com.sankuai.meituan") # Close APP
Element Positioning
The attributes of the selected elements can be used to locate. The purpose of these attributes is to get a unique locating element. Attributes can be combined to locate. Common locations are as follows
driver(text="Sports Health") # Locate by text driver(description="My") # Locate by description driver.xpath('//*[@text="Today's special"]') # Locate by Xpath driver(className="android.widget.ImageView") # Locate by className driver(resourceId="com.sankuai.meituan:id/button") # Locate by ID driver(resourceId="com.android.systemui:id/tile_label", text="Power saving mode") # Locate by combination
You can also locate nodes in the hierarchy as follows
# In android. Widget. Find className=android in the sibling node of GridLayout. View. Elements of View driver(className="android.widget.GridLayout").sibling(className="android.view.View") # In android. Widget. Find the fourth className=android in a child node of GridLayout. View. Elements of View driver(className="android.widget.GridLayout").child_by_instance(3,className="android.view.View") # In android. Widget. Find className=android in the child node of LinearLayout. Widget. TextView element with text "cycling", allow_scroll_search=True means allow sliding screen to find driver(className="android.widget.LinearLayout")\ .child_by_text("Cycling",allow_scroll_search=True,className="android.widget.TextView") # In android. Widget. Find className=android in the child node of FrameLayout. View. Element of ViewGroup described as Comment Illustration driver(className="android.widget.FrameLayout")\ .child_by_description("Comment illustration",allow_scroll_search=True,className="android.view.ViewGroup") # Locate elements by up/down/left/right, as follows: Locate and click APP to the right of NetEase Cloud Music APP driver(text="NetEase cloud music").right(className="android.widget.TextView").click()
Assertion
Use assert to determine whether the actual results are consistent with the expected results
# Get hints about login failure text = driver(resourceId="com.sankuai.meituan:id/passport_account_tips").get_text() # Determine if the prompt is correct assert text == "Account or password error, please re-enter" # Since the growth value does not appear until the login is successful, check to see if the growth value element exists to determine whether the login is successful assert driver(resourceId="com.sankuai.meituan:id/grouth_tv").exists
Use toast for assertions with no focus, a time-limited prompt box
tips = driver.toast.get_message() # Get error message assert "ERROR Incorrect username or password" in tips # Determine if there is a "user name or password error" in the information you get
Case Demonstration
Example 1: Setting the graphics authentication code and unlocking, the real machine could not be implemented, and could not enter the unlocking interface, which may be a permission problem, and was executed successfully with the simulator
import time import uiautomator2 as u2 driver = u2.connect() # Connect device driver(text="Set up").click() # Click Set APP # _Locate elements using node mode driver(className="android.widget.LinearLayout").child_by_text("security", allow_scroll_search=True,className="android.widget.TextView").click() driver(resourceId="android:id/title", text="Screen lock").click() # Locate elements using ID # _Locate elements using xpath driver.xpath('//*[@resource-id="com.android.settings:id/list"]/android.widget.LinearLayout[3]/android.widget.RelativeLayout[1]').click() driver.swipe_points([(0.223, 0.658), (0.5, 0.832), (0.5, 0.652), (0.78, 0.487)], 0.2) # Multipoint Slide, Set Graphic Password driver(resourceId="com.android.settings:id/footerRightButton").click() # Click Continue driver.swipe_points([(0.223, 0.658), (0.5, 0.832), (0.5, 0.652), (0.78, 0.487)], 0.2) driver(resourceId="com.android.settings:id/footerRightButton").click() # Click OK driver(resourceId="com.android.settings:id/redaction_done_button").click() # Click Finish driver.press("power") # Click on the power key time.sleep(3) # Too fast will only turn off and light up the screen, so wait 3 seconds to lock the device driver.unlock() # Unlock operation driver.swipe_points([(0.273, 0.728), (0.5, 0.867), (0.5, 0.728), (0.719, 0.591)], 0.2) # Draw graphic password assert driver(text="security").exists # Determine whether to return to a secure page
Example 2: Take Meituan APP as an example, to test login, view orders, search for commodities related operations, because there are a large number of positioning element operations, it is not easy to maintain without using the framework, so use Pytest+Allure and PO model to achieve this operation, the structure is as follows:
In the case of login, create the base class BasePage first. Py file, encapsulates some common methods, such as: locate, click, enter, clear, get text information, assert, etc.
import re class BasePage(): # Constructor def __init__(self, driver): self.driver = driver def click(self, element): # click if str(element).startswith("com"): # Use ID location if starting with com self.driver(resourceId=element).click() # Click to locate elements elif re.findall("//", str (element): #If // begins with regular expression matching and then positioned with xpath" self.driver.xpath(element).click() # Click to locate elements else: # If neither of the above is true, use description to locate self.driver(description=element).click() # Click to locate elements def click_text(self, element): # Click to locate according to text self.driver(text=element).click() # Click to locate elements def clear(self, element): # Empty the contents of the input box if str(element).startswith("com"): # Use ID location if starting with com self.driver(resourceId=element).clear_text() # Clear Text elif re.findall("//", str (element): #If // begins with regular expression matching and then positioned with xpath" self.driver.xpath(element).clear_text() # Clear Text else: # If neither of the above is true, use description to locate self.driver(description=element).clear_text() # Clear Text def find_elements(self, element, timeout=5): # Find Elements is_exited = False try: while timeout > 0: xml = self.driver.dump_hierarchy() # Get Page Hierarchy if re.findall(element, xml): is_exited = True break else: timeout -= 1 except: print("Element not found!") finally: return is_exited def assert_exited(self, element): # Assert whether an element exists assert self.find_elements(element) == True, "The assertion failed,{}Element does not exist!".format(element)
Then create a way to encapsulate the appropriate page actions, such as a login operation, and create a login_page.py file, this layer is mainly the encapsulation operation flow
from base.basepage import BasePage # Importing methods encapsulated in base classes import allure # Import allure class LoginPage(BasePage): # Element Location, Element Location Information agreement = "com.sankuai.meituan:id/permission_agree_btn" get_loc_info = "com.android.permissioncontroller:id/permission_allow_foreground_only_button" get_pho_perm = "com.android.permissioncontroller:id/permission_deny_button" notice = "Not yet" login_in_now = "com.sankuai.meituan:id/button" pwd_login = "com.sankuai.meituan:id/user_password_login" username = "com.sankuai.meituan:id/passport_mobile_phone" password = "com.sankuai.meituan:id/edit_password" tick = "com.sankuai.meituan:id/passport_account_checkbox" click_login = "com.sankuai.meituan:id/login_button" mine = "My" growth = "com.sankuai.meituan:id/grouth_tv" # Behavior, Page Action @allure.story('Test Meituan APP') @allure.title("APP Sign in") def login(self,user,pwd): # Login process, for example, running APP for the first time self.click(self.agreement) # Agree to use the APP protocol self.click(self.get_loc_info) # Get the location information and select [Allow when using] self.click(self.get_pho_perm) # Get phone permissions, select Reject self.click_text(self.notice) # Whether to turn on message notifications, select "Not yet" self.click(self.login_in_now) # Click on [Sign in now] on the first page self.click(self.pwd_login) # Login mode selection [password login] self.input(self.username,user) # enter one user name self.input(self.password,pwd) # Input password self.click(self.tick) # Check to agree to user agreement self.click(self.click_login) # Click Login self.click(self.mine) # Click [My] self.assert_exited(self.growth) # Asserts that the growth value is only displayed because the login was successful, so whether or not the login was successful is determined by the presence of this element self.click(self.homepage) # Cut to Home Page
Finally, pass in the parameter and perform the use-case operation, because the Pytest framework is used, so this file takes care of the format, and the file name needs to start with test to create test_login.py file
import uiautomator2 as u2 import pytest from pageobject.login_page import LoginPage # Import the classes from the previous action flow # Connect your phone and start APP for login class TestLogin: # Class begins with Test def test_login(self): # Method to test_ Or _ Beginning of test driver = u2.connect("48fd6742") driver.app_start("com.sankuai.meituan", stop=True) login_page = LoginPage(driver=driver) login_page.login("16666666666","123456abc")
Once the login operation is complete, you can execute the test.
The same is true for search operations, which write common methods directly to the base class file and create search_separately by the process Page. Py file
from base.basepage import BasePage # Import base class and call public methods directly import allure class SearchPage(BasePage): # Element Positioning Information inputfield = "com.sankuai.meituan:id/search_edit_flipper_container" clicksearch = "com.sankuai.meituan:id/search" inputvalue = "com.sankuai.meituan:id/search_edit" back = "com.sankuai.meituan:id/back" allure.title("search") def search(self,value): # Search Operational Process self.click(self.inputfield) # Click on the search box self.clear(self.inputvalue) # Empty the search box on each search front self.input(self.inputvalue,value) # Locate the input box and enter keywords self.click(self.clicksearch) # Click the Search button self.click(self.back) # Return to previous level self.click(self.back) # Return to Home Page
Create a file test_to pass parameters and perform search operations Search. PY
import uiautomator2 as u2 import pytest from pageobject.search_page import SearchPage # Import the classes from the previous action flow class TestSearch: keyword = ["Tea with milk","Table tennis","Scenic spot"] @pytest.mark.parametrize("value",keyword) # Using pytest parameterization, search for three keywords def test_search(self,value): driver = u2.connect("48fd6742") driver.app_start("com.sankuai.meituan") search_page = SearchPage(driver=driver) search_page.search(value)
Use allure to execute tests and generate reports
pytest --alluredir=./result testcases # Execute all test cases under the testcases file and generate intermediate results into the result directory allure serve ./result # Generate final report
Check out the usage of Pytest, Allure This article
See about PO models This article