当前位置:网站首页>Appium click operation sorting
Appium click operation sorting
2022-07-28 02:12:00 【Xiao Liu xuezhuo】
Appium Execute in the client script of the framework UI The principle of operation is : Need to execute in script UI In operation , Will send a http request ( The request data includes control related information ) To Appium Server for , The server will escape the received data again , Then forward it to the plug-in program installed on the mobile phone . At this time, the pile insertion program calls android sdk Provided uiautomator relevant ui Operate the library to perform real UI operation . Then the results are returned to the script along the way to form a closed loop .
Now pay attention to analyzing how to give Appium Server send http request .
1、 start-up Appim service
Start first Appium service , Give Way Appium Service listening port 4723, So the script can send to this port http Request the ;
2、 Before executing the use case in the script, you need to create webDriver object
This object can be understood as appium Provided to execute in the script UI Encapsulated function library of operation .
webDriver Class construction methods will be based on ”desired capabilities“ Information direction appium The server initiated a request , Server get ”desired capabilities“ Based on this information, a SessionId And return to the use case script .
also Appium Get ”desired capabilities“ Then you can know which connection you want to connect PC Your mobile device is connected .Appium The service will do many things here to ensure the successful connection with the plug-in service program on the mobile terminal . Please refer to another article of mine for specific things done ”appium Sort out the process from start to test and then to end “.
You may ask what you want to get before executing the use case session id Well ?
Because when executing the test , Script use cases are essentially sent to the server http request , however http Request is stateless , Every... Received by the server http Requests are considered to have nothing to do with previous requests . This will lead to every http All requests must be brought ”desired capabilities“ Information , Only in this way can the server know which mobile device to communicate with , also ”desired capabilities“ There are many other information , To ensure appium Service according to ”desired capabilities“ Run with the specified parameters . That is, we need to ensure that every http The requested running environment is consistent .
Every one of them http Please bring ”desired capabilities“ Information this is obviously not desirable . therefore appium What is taken is session Mechanism .appium Get the service for the first time ”desired capabilities“ There will be local , And return a session id Give script use cases , In this way, subsequent use cases will be sent http Ask to appium service , be appium Service basis session id You can know which one corresponds ”desired capabilities“, Then you can know what the running environment is like ( The operating environment refers to which mobile phone device to communicate with , Use case execution delays or retries these in ”desired capabilities“ The information specified in ).
One 、 obtain webdriver object , And get the session id
self.caps = {}
self.caps["platformName"] = "Android"
self.caps["platformVersion"] = devices.dev[Constant.phone]["platformVersion"]
self.caps["deviceName"] = devices.dev[Constant.phone]["phone"]
self.caps["appPackage"] = Constant.appPackage
self.caps["appActivity"] = Constant.appActivity
self.caps['app'] = Constant.app
self.caps["unicodeKeyboard"] = True
self.caps["autoAcceptAlerts"] = True # Authorize the permission pop-up
self.caps["resetKeyboard"] = True
self.caps["noReset"] = True
self.caps["newCommandTimeout"] = 6000
self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', self.caps) # localhostIt's on it appium A dictionary is used to store ”desired capabilities“, And then I created one webDriver object .
'http://127.0.0.1:4723/wd/hub' It represents the local 4723 Port send request ,appium The port monitored by the service runtime is 4723,url The path part of /wd/hub, among wd yes webdriver Abbreviation ,hub Represents the central node . These in appium Service node.js The corresponding path can be found in the source code .
class WebDriver(webdriver.Remote):
def __init__(self, command_executor='http://127.0.0.1:4444/wd/hub',
desired_capabilities=None, browser_profile=None, proxy=None, keep_alive=False):
super(WebDriver, self).__init__(command_executor, desired_capabilities, browser_profile, proxy, keep_alive)
if self.command_executor is not None:
self._addCommands()class WebDriver(object):
def __init__(self, command_executor='http://127.0.0.1:4444/wd/hub',
desired_capabilities=None, browser_profile=None, proxy=None,
keep_alive=False, file_detector=None):
...
if type(self.command_executor) is bytes or isinstance(self.command_executor, str):
self.command_executor = RemoteConnection(command_executor, keep_alive=keep_alive)
...
self.start_session(desired_capabilities, browser_profile)
...Look at the simple one first , When self.command_executor Not for None when , call self._addCommands() Go to self.command_executor._commands Add some commands to the command mapping list . because appium Is based on selenium Secondary developed ,self.command_executor._commands yes selenium The original command word mapping table in the framework ,appium On this basis, some new .
Then the main thing in the constructor is to create RemoteConnection Objects and start_session().
1、 establish RemoteConnection Object and assign to command_executor attribute
Let's see RemoteConnection Constructor , It is found that the internal part is mainly to check what we first introduced url Whether the parameter format of is correct . And put url After parsing, assign values to your own attributes and save . And use _commands Dictionaries To preserve http Request method and url route ( This should be to prepare for the future ). In the form of : Command description string -->(http Request method ,http Request path ). such You only need to know the command request method and path as long as the command description string .
self._commands = {
Command.STATUS: ('GET', '/status'),
Command.NEW_SESSION: ('POST', '/session'),
Command.GET_ALL_SESSIONS: ('GET', '/sessions'),
Command.QUIT: ('DELETE', '/session/$sessionId'),
Command.GET_CURRENT_WINDOW_HANDLE:
('GET', '/session/$sessionId/window_handle'),
Command.GET_WINDOW_HANDLES:
('GET', '/session/$sessionId/window_handles'),
Command.GET: ('POST', '/session/$sessionId/url'),
Command.GO_FORWARD: ('POST', '/session/$sessionId/forward'),
Command.GO_BACK: ('POST', '/session/$sessionId/back'),
Command.REFRESH: ('POST', '/session/$sessionId/refresh'),
Command.EXECUTE_SCRIPT: ('POST', '/session/$sessionId/execute'),
Command.GET_CURRENT_URL: ('GET', '/session/$sessionId/url'),
Command.GET_TITLE: ('GET', '/session/$sessionId/title'),
Command.GET_PAGE_SOURCE: ('GET', '/session/$sessionId/source'),
Command.SCREENSHOT: ('GET', '/session/$sessionId/screenshot'),
Command.ELEMENT_SCREENSHOT: ('GET', '/session/$sessionId/element/$id/screenshot'),
Command.FIND_ELEMENT: ('POST', '/session/$sessionId/element'),
Command.FIND_ELEMENTS: ('POST', '/session/$sessionId/elements'),
...Command Class holds all the commands in the use case
class Command(object):
"""
Defines constants for the standard WebDriver commands.
While these constants have no meaning in and of themselves, they are
used to marshal commands through a service that implements WebDriver's
remote wire protocol:
https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol
"""
# Keep in sync with org.openqa.selenium.remote.DriverCommand
STATUS = "status"
NEW_SESSION = "newSession"
GET_ALL_SESSIONS = "getAllSessions"
DELETE_SESSION = "deleteSession"
CLOSE = "close"
QUIT = "quit"
GET = "get"
GO_BACK = "goBack"
GO_FORWARD = "goForward"
REFRESH = "refresh"
ADD_COOKIE = "addCookie"
GET_COOKIE = "getCookie"
GET_ALL_COOKIES = "getCookies"
DELETE_COOKIE = "deleteCookie"
DELETE_ALL_COOKIES = "deleteAllCookies"
FIND_ELEMENT = "findElement"
FIND_ELEMENTS = "findElements"
FIND_CHILD_ELEMENT = "findChildElement"
FIND_CHILD_ELEMENTS = "findChildElements"
CLEAR_ELEMENT = "clearElement"
CLICK_ELEMENT = "clickElement"
SEND_KEYS_TO_ELEMENT = "sendKeysToElement"
SEND_KEYS_TO_ACTIVE_ELEMENT = "sendKeysToActiveElement"
SUBMIT_ELEMENT = "submitElement"
UPLOAD_FILE = "uploadFile"
GET_CURRENT_WINDOW_HANDLE = "getCurrentWindowHandle"
GET_WINDOW_HANDLES = "getWindowHandles"
...In this way, the command keyword is associated with the command .
2、 perform start_session(desired_capabilities, browser_profile) Generate session id
def start_session(self, desired_capabilities, browser_profile=None):
"""
Creates a new session with the desired capabilities.
:Args:
- browser_name - The name of the browser to request.
- version - Which browser version to request.
- platform - Which platform to request the browser on.
- javascript_enabled - Whether the new session should support JavaScript.
- browser_profile - A selenium.webdriver.firefox.firefox_profile.FirefoxProfile object. Only used if Firefox is requested.
"""
capabilities = {'desiredCapabilities': {}, 'requiredCapabilities': {}}
for k, v in desired_capabilities.items():
if k not in ('desiredCapabilities', 'requiredCapabilities'):
capabilities['desiredCapabilities'][k] = v
else:
capabilities[k].update(v)
if browser_profile:
capabilities['desiredCapabilities']['firefox_profile'] = browser_profile.encoded
response = self.execute(Command.NEW_SESSION, capabilities)
if 'sessionId' not in response:
response = response['value']
self.session_id = response['sessionId']
self.capabilities = response['value']
# Quick check to see if we have a W3C Compliant browser
self.w3c = response.get('status') is Noneamong response = self.execute(Command.NEW_SESSION, capabilities) perform .
appium Sent in http All requests are in excute() Method execution .excute() Again in command_executor.execute(), Finally, in this execute() Call in request send out http request .
def execute(self, driver_command, params=None):
"""
Sends a command to be executed by a command.CommandExecutor.
:Args:
- driver_command: The name of the command to execute as a string.
- params: A dictionary of named parameters to send with the command.
:Returns:
The command's JSON response loaded into a dictionary object.
"""
if self.session_id is not None:
if not params:
params = {'sessionId': self.session_id}
elif 'sessionId' not in params:
params['sessionId'] = self.session_id
params = self._wrap_value(params)
response = self.command_executor.execute(driver_command, params)
if response:
self.error_handler.check_response(response)
response['value'] = self._unwrap_value(
response.get('value', None))
return response
# If the server doesn't send a response, assume the command was
# a success
return {'success': 0, 'value': None, 'sessionId': self.session_id}
You can see , The function interior will first check session_id Is it for none. In obtaining sessioid When , This session_id yes none, Go straight to the following logic . Get it session All subsequent requests ,sessioid Not for null, Then the parameters will be checked params add sessionid Parameters . So the server knows which client the request comes from .
The original client's session id It's here to get , And add here every time you request session id Yeah !
The call process is a little complicated , Let's have a flowchart .

Two 、 Execute find control and execute UI operation
Use a basic click operation to sort out this process .
self.driver.find_element_by_id("xxx").click()1、driver.find_element_by_id() Get controls
def find_element_by_id(self, id_):
"""Finds an element by id.
:Args:
- id\_ - The id of the element to be found.
:Usage:
driver.find_element_by_id('foo')
"""
return self.find_element(by=By.ID, value=id_)
Its internal call is its own find_element() Method
def find_element(self, by=By.ID, value=None):
"""
'Private' method used by the find_element_by_* methods.
:Usage:
Use the corresponding find_element_by_* instead of this.
:rtype: WebElement
"""
if self.w3c:
if by == By.ID:
by = By.CSS_SELECTOR
value = '[id="%s"]' % value
elif by == By.TAG_NAME:
by = By.CSS_SELECTOR
elif by == By.CLASS_NAME:
by = By.CSS_SELECTOR
value = ".%s" % value
elif by == By.NAME:
by = By.CSS_SELECTOR
value = '[name="%s"]' % value
return self.execute(Command.FIND_ELEMENT, {
'using': by,
'value': value})['value']Here, according to different search methods , Yes by and value Parameters are processed , Then call its own excute() Method . Pay attention to the notes rtype: WebElement, explain find_element() Back to a WebElement object .
self.execute(Command.FIND_ELEMENT, {'using': by,'value': value})['value']You need to go excute() See how to return a WebElement Object .
def execute(self, driver_command, params=None):
"""
Sends a command to be executed by a command.CommandExecutor.
:Args:
- driver_command: The name of the command to execute as a string.
- params: A dictionary of named parameters to send with the command.
:Returns:
The command's JSON response loaded into a dictionary object.
"""
if self.session_id is not None:
if not params:
params = {'sessionId': self.session_id}
elif 'sessionId' not in params:
params['sessionId'] = self.session_id
params = self._wrap_value(params)
response = self.command_executor.execute(driver_command, params)
if response:
self.error_handler.check_response(response)
response['value'] = self._unwrap_value(
response.get('value', None))
return response
# If the server doesn't send a response, assume the command was
# a success
return {'success': 0, 'value': None, 'sessionId': self.session_id}
You can see through the comments ,excute() Sent a command to be executed to command.CommandExecutor, Then get the return result response. also response yes json Format dictionary type .
excute() The first method is to params Parameter plus session id, This has been analyzed before .
Then packaging params Parameters , And implement command_executor.execute(driver_command, params) Get back response. Then check response Whether these formats are normal , If response There are no errors in the , On the other hand response Medium value Unpack . It is in this place that WebElement Object's .
def _unwrap_value(self, value):
if isinstance(value, dict) and ('ELEMENT' in value or 'element-6066-11e4-a52e-4f735466cecf' in value):
wrapped_id = value.get('ELEMENT', None)
if wrapped_id:
return self.create_web_element(value['ELEMENT'])
else:
return self.create_web_element(value['element-6066-11e4-a52e-4f735466cecf'])
elif isinstance(value, list):
return list(self._unwrap_value(item) for item in value)
else:
return valueamong create_web_element() The internal implementation is
def create_web_element(self, element_id):
"""Creates a web element with the specified `element_id`."""
return self._web_element_cls(self, element_id, w3c=self.w3c)
and _web_element_cls by
_web_element_cls = WebElementtherefore , Finally understand how to call find_element_by_id() Step by step, how to finally get WebElement 了 .
2、WebElement Object click() Click on
def click(self):
"""Clicks the element."""
self._execute(Command.CLICK_ELEMENT)Enter into _excute()
# Private Methods
def _execute(self, command, params=None):
"""Executes a command against the underlying HTML element.
Args:
command: The name of the command to _execute as a string.
params: A dictionary of named parameters to send with the command.
Returns:
The command's JSON response loaded into a dictionary object.
"""
if not params:
params = {}
params['id'] = self._id
return self._parent.execute(command, params)self._parent What is the object ? stay WebElement In the constructor of ,self._parent Is the first parameter passed in by the constructor
class WebElement(object):
def __init__(self, parent, id_, w3c=False):
self._parent = parent
self._id = id_
self._w3c = w3cThen go back to the above to create WebElement The place of , Found that the incoming is WebDriver object
def create_web_element(self, element_id):
"""Creates a web element with the specified `element_id`."""
return self._web_element_cls(self, element_id, w3c=self.w3c)
def _unwrap_value(self, value):
if isinstance(value, dict) and ('ELEMENT' in value or 'element-6066-11e4-a52e-4f735466cecf' in value):
wrapped_id = value.get('ELEMENT', None)
if wrapped_id:
return self.create_web_element(value['ELEMENT'])
else:
return self.create_web_element(value['element-6066-11e4-a52e-4f735466cecf'])
elif isinstance(value, list):
return list(self._unwrap_value(item) for item in value)
else:
return valuetherefore self._parent.execute(command, params) The call is still webdriver Object's excute() Method .
Come here , It clarifies how the control is clicked , Its essence is also to appium The service sends a http request .
边栏推荐
- WMS you don't know
- Flex development web page instance web side
- N32l43x FLASH read \ write \ erase operation summary
- 54:第五章:开发admin管理服务:7:人脸入库流程;人脸登录流程;浏览器开启视频调试模式(以便能够在本机的不安全域名的情况下,也能去开启摄像头);
- Leetcode's 83rd biweekly match
- 数据输出-绘制动图
- Gbase 8C backup control function (III)
- Hcip day 12 notes
- Starfish Os打造的元宇宙生态,跟MetaBell的合作只是开始
- The principle and implementation of loss function cross entropy
猜你喜欢

Packet capturing wizard netcapture app packet capturing tutorial "complete"

Machine learning how to achieve epidemic visualization -- epidemic data analysis and prediction practice

Behind every piece of information you collect, you can't live without TA

FreeRTOS kernel summary

Unreal ue4.27 switchboard porting engine process

54: Chapter 5: develop admin management services: 7: face warehousing process; Face login process; The browser turns on the video debugging mode (so that the camera can also be turned on in the case o

Synchronized details

Five basic data structures of redis

测试/开发程序员的级别“陷阱“,级别不是衡量单维度的能力......

【数据库数据恢复】SQL Server数据库磁盘空间不足的数据恢复案例
随机推荐
The principle and implementation of loss function cross entropy
Data security and privacy computing summit - provable security: Learning
mongodb/mongoTemplate.upsert批量插入更新数据的实现
Machine learning how to achieve epidemic visualization -- epidemic data analysis and prediction practice
软件测试面试题:常见的 POST 提交数据方式
They are all talking about Devops. Do you really understand it?
华为APP UI自动化测试岗面试真题,真实面试经历。
Leveraging the blue ocean of household appliances consumption with "digital channels", the dealer online system enables enterprises to further their business
go 学习01
如何评估研发人员效能?软件工程师报告帮你看见每个人的贡献
synchronized详解
Gbase 8C configuration setting function
Gbase 8C backup control function (III)
萤石网络,难当「孤勇者」
二叉树的遍历和性质
Flume(5个demo轻松入门)
How to evaluate the effectiveness of R & D personnel? Software Engineer reports help you see everyone's contribution
[database data recovery] data recovery case of insufficient disk space of SQL Server database
Gbase 8C backup control function (I)
Gbase 8C transaction ID and snapshot (V)