基本元素操作

click(self):

点击元素

# 点击元素
driver.find_element('xpath','//*[@text='我的柠檬']').click()

send_keys(self,*value):

在元素中输入文本内容

# 在元素中输入文本信息
element = driver.find_element('id','com.lemon.lemonban:id/et_mobile')
element.send_keys('18843387465')

clear(self):

在元素中清除文本内容

# 在元素中清除文本内容
element = driver.find_element('id','com.lemon.lemonban:id/et_mobile')
element.send_keys('18843387465')
element.clear()

submit(self):

提交表单操作,在H5和webview中可使用

获取元素属性操作

  1. text:获取元素属性中的text值
  2. tag_name:获取元素的标签名(原生应用无标签名,所以为None)
  3. get_attribute(self,*name):根据属性名获取元素属性,属性名填写错误会报错
  4. size:获取元素的宽和高,返回字典
  5. location:获取元素左上角的坐标,返回字典
  6. rect:元素的大小和位置组合字典,返回字典
  7. context:返回当前会话的当前上下文,使用后可以识别H5页面的控件
  8. contexts:返回当前会话的上下文

备注:还有部分方法如screenshot(self,*filename)等用在H5和webview端,由于appium中api没有区分原生和H5,所以当在代码中报错Methodhasnotyetbeenimplemented时,可以确认该方法在另外一端使用

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2021/8/14 16:54
# @Author  : shisuiyi
# @File    : app_kaoyan.py
# @Software: win10 Tensorflow1.13.1 python3.7
from appium import webdriver
import time

# 1.设置终端参数
desired_caps = {
    "platformName": "Android",
    "platformVersion": "7.1.2",
    "deviceName": "模拟器",
    "appPackage": "com.lemon.lemonban",
    "appActivity": "com.lemon.lemonban.activity.MainActivity",
    "noReset": "True",
    'skipServerInstallation': "True",
    'automationName': 'UiAutomator2'
}

# 2.appium server 进行启动
# 3. 发送指令到appium server
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)

time.sleep(3)
element = driver.find_element('xpath', "//*[@text='我的柠檬']")
# 获取元素属性中的text值
print(element.text)
# 获取元素的标签名
print(element.tag_name)
# 根据属性名获取元素属性
print(element.get_attribute('checkable'))
# 获取元素的宽和高,返回字典
print(element.size)
# 获取元素左上角的坐标,返回字典
print(element.location)
# 元素的大小和位置组合字典,返回字典
print(element.rect)
# H5 环境
# 返回当前会话的当前上下文,使用后可以识别H5页面的控件
# print(driver.context)
# 返回当前会话中的上下文,使用后可以识别H5页面的控件
# print(driver.contexts)

输出:

我的柠檬
None
false
{'height': 30, 'width': 88}
{'x': 832, 'y': 1887}
{'x': 832, 'y': 1887, 'width': 88, 'height': 30}

Process finished with exit code 0

元素判断的操作

  1. is_displayed(self):此元素是否可见。隐藏元素和被控件挡住无法操作的元素(仅限H5和webview支持)返回布尔值;
# 该元素是否可见
is_d = driver.find_element('xpath', "//*[@text='我的柠檬']").is_displayed()
print(is_d)
  1. is_enabled(self):此元素是否可用。元素灰色和无法操作的元素(仅限H5和webview支持)返回布尔值;
# 该元素是否可用
is_e = driver.find_element('xpath','xxxx').is_enabled()
print(is_e)
  1. is_selected(self):此元素是否被选中。适用于单选框、复选框等类型的元素(仅限H5和webview支持)返回布尔值;
# 此元素是否被选中
is_s = driver.find_element('xpath','xxxx"]').is_selected()
print(is_s)
  1. 对于原生应用来讲,元素断言可以通过获取displayed、enabled、selected属性值,然后通过条件语句if来进行判断;

TouchAction 类型的操作

按压控件press()

开始按压一个元素或坐标点(x,y)。通过手指按压手机屏幕的某个位置。

from appium.webdriver.common.touch_action import TouchAction # 需要导入 TouchAction
'''
release():结束的行动取消屏幕上的指针。
Perform():执行的操作发送到服务器的命令操作。
'''
TouchAction(driver).press(x=0,y=308).release().perform()  # press 也可以接收屏幕的坐标(x,y)

长按控件· long_press()

开始按压一个元素或坐标点(x,y)。 相比press()方法,long_press()多了一个入参,既然长按,得有按的时间吧。duration 以毫秒为单位。1000 表示按一秒钟。其用法与 press() 方法相同。

from appium.webdriver.common.touch_action import TouchAction # 需要导入 TouchAction
TouchAction(driver).long_press(x=100, y=200, duration=3000).perform() #duration单位是ms

点击控件· tap()

对一个元素或控件执行点击操作。用法参考 press()。

from appium.webdriver.common.touch_action import TouchAction # 需要导入 TouchAction
TouchAction(driver).tap(1 ,302).perform();

移动· move_to()

move_to(el=None, x=None, y=None)这里的x,y为前一个坐标的偏移量
参数:

  • el:定位的元素
  • x:相对于前一个元素的x轴偏移量
  • y:相对于前一个元素的y轴偏移量

将指针(光标)从过去指向指定的元素或点。

  1. 指向指定的元素
# 定位到存储
el = driver.find_element_by_xpath("//*[contains(@text,'存储')]")
# 定位到更多
el1 = driver.find_element_by_xpath("//*[contains(@text,'更多')]")
# 元素方式滑动
TouchAction(driver).press(el).move_to(el1).release().perform()
  1. 指向相应点的坐标(相对坐标和绝对坐标均可)
# 坐标的方式向上滑动
TouchAction(driver).press(x=240,y=1000).move_to(x=0,y=-400).release().perform() 
# press().move_to() 实际使用的是TouchAction方法,需要给相对坐标.

绘制图案四个坐标

 # 绘制图案四个坐标 1:(244,967) 2:(723,967) 3:(723,1442) 4:(244,1916)
 TouchAction(driver).press(x=244,y=967).wait(100).move_to(x=479,y=0).wait(100)\
            .move_to(x=0,y=475).wait(100).move_to(x=-479,y=474).release().perform()

暂停· wait()

暂停脚本的执行,单位为毫秒。

from appium.webdriver.common.touch_action import TouchAction # 需要导入 TouchAction

TouchAction(driver).wait(1000)

松开· release()

松开press

执行 · perform

每个动作需要这个函数才能执行

滑屏操作 swipe

从一个点滑到另外一个点,那么每个点都有 x 值和 y 值。swipe有 4 个参数。所以起始 x,起始 y,结束 x,结束 y。

def swipe(self: T, start_x: int, start_y: int, end_x: int, end_y: int, duration: int = 0) -> T:
  1. 先获取设备的屏幕的大小get_window_size()这个函数的返回值
  2. 再设置滑动的距离和屏幕大小的百分比
  3. 调动滑动接口来执行滑动操作
window_size = driver.get_window_size()
print("手机屏幕的尺寸:", window_size)
x = window_size["width"]
y = window_size["height"]
driver.swipe(x*0.9, y*0.5, x*0.1, y*0.5, 2000)

滑动的封装

class BasePage:
    def __init__(self, driver):
        self.driver = driver

    def width(self):
        """获取当前屏幕的宽度"""
        size = self.driver.get_window_size()
        return size['width']

    def height(self):
        """获取当前屏幕的高"""
        size = self.driver.get_window_size()
        return size['height']

    def swipe_left(self):
        """从右到左滑动"""
        # 算出手机的分辨率, 控制滑动的比例是根据手机的分辨率
        width = self.width()
        height = self.height()
        self.driver.swipe(width * 0.95, height * 0.5, end_x=width * 0.05, end_y=height * 0.5)

    def swipe_right(self):
        """从左到右滑动"""
        # 算出手机的分辨率, 控制滑动的比例是根据手机的分辨率
        width = self.width()
        height = self.height()
        self.driver.swipe(width * 0.05, height * 0.5, end_x=width * 0.95, end_y=height * 0.5)

    def swipe_down(self):
        """从上到下"""
        # 算出手机的分辨率, 控制滑动的比例是根据手机的分辨率
        width = self.width()
        height = self.height()
        self.driver.swipe(width * 0.5, height * 0.05, end_x=width * 0.5, end_y=height * 0.95)

    def swipe_up(self):
        """从下到上"""
        # 算出手机的分辨率, 控制滑动的比例是根据手机的分辨率
        width = self.width()
        height = self.height()
        self.driver.swipe(width * 0.5, height * 0.95, end_x=width * 0.5, end_y=height * 0.05)


# 表示告诉appium 服务连接哪个手机, 手机系统信息, 你要测哪个 app
caps = {
    "platformName": "Android",
    "udid": "emulator-5554",
    "app": file,
    # "appPackage": "com.lemon.lemonban",  # 包名, 每个app在手机上面的标志。 id
    # "appActivity": ".activity.WelcomeActivity",
    # "platformVersion": '4',
    # "noRest": True,   # 不重启app的状态
}

driver = Remote(desired_capabilities=caps,
                command_executor = 'http://127.0.0.1:4723/wd/hub')

time.sleep(10)
# 滑动
# swipe_left(driver)
page = BasePage(driver)
page.swipe_left()
page.swipe_left()
page.swipe_left()
time.sleep(3)
# # 点击操作,点击,
driver.quit()

放大缩小操作

在 appium2.0 之前,在移动端设备上的触屏操作,单手指触屏和多手指触屏分别是由 TouchAction 类,Multiaction 类实现的。
在 appium2.0 之后,这 2 个方法将会被舍弃。
在 w3c 的 actions 当中,将输入源分为了三类:

  1. 键盘类 - Key
  2. 指针类 - Pointer
  3. None
from selenium.webdriver import ActionChains
from selenium.webdriver.common.actions.mouse_button import MouseButton


def zoom(driver: Remote):
    actions = ActionChains(driver)
    actions.w3c_actions.devices = [] # 输入源设备列表为空
    finger1 = actions.w3c_actions.add_pointer_input('touch', 'finger1') # 添加一个新的指针输入源
    finger2 = actions.w3c_actions.add_pointer_input('touch', 'finger2')

    width = driver.get_window_size()['width']
    height = driver.get_window_size()['height']

    # 两个手指移动到屏幕正中间
    # PointerMove:移动到屏幕某个点
    finger1.create_pointer_move(x=width*0.5, y=height*0.5)
    finger2.create_pointer_move(x=width * 0.5, y=height * 0.5)
    # 两个手指按下去
    # PointerDown:按下鼠标键,或者触屏或者触屏笔触屏
    finger1.create_pointer_down(MouseButton.LEFT)
    finger2.create_pointer_down(MouseButton.LEFT)
    # 两个手指移动
    finger1.create_pointer_move(x=width*0.5, y=height * 0.9)
    finger2.create_pointer_move(x=width * 0.5, y=height *0.1)
    # 两个手指松开
    # PointerUp:松开鼠标键,或者手离开屏幕,或者触屏笔离开屏幕
    finger1.create_pointer_up(MouseButton.LEFT)
    finger2.create_pointer_up(MouseButton.LEFT)

    actions.perform()


def suoxiao(driver: Remote):
    """缩小"""
    actions = ActionChains(driver)
    actions.w3c_actions.devices = []
    finger1 = actions.w3c_actions.add_pointer_input('touch', 'finger1')
    finger2 = actions.w3c_actions.add_pointer_input('touch', 'finger2')

    width = driver.get_window_size()['width']
    height = driver.get_window_size()['height']

    # 两个手指移动到屏幕正中间
    finger1.create_pointer_move(x=width*0.5, y=height*0.9)
    finger2.create_pointer_move(x=width * 0.5, y=height * 0.1)
    # 两个手指按下去
    finger1.create_pointer_down(MouseButton.LEFT)
    finger2.create_pointer_down(MouseButton.LEFT)
    # 两个手指移动
    finger1.create_pointer_move(x=width*0.5, y=height * 0.5)
    finger2.create_pointer_move(x=width * 0.5, y=height *0.5)
    # 两个手指松开
    finger1.create_pointer_up(MouseButton.LEFT)
    finger2.create_pointer_up(MouseButton.LEFT)

    actions.perform()

Keyboard 相关

Keyboard 类在 appium\webdriver\extensions 下的 keyboard.py 模块中, 属于键盘操作类

  • driver.press_keycode()
  • driver.long_press_keycode()
  • driver.hide_keyboard()
  • driver.keyevent()
  • driver.is_keyboard_shown()

driver.press_keycode()

源码:

def press_keycode(self: T, keycode: int, metastate: Optional[int] = None, flags: Optional[int] = None) -> T:
    """Sends a keycode to the device.

    Android only. Possible keycodes can be found
    in http://developer.android.com/reference/android/view/KeyEvent.html.

    Args:
        keycode: the keycode to be sent to the device
        metastate: meta information about the keycode being sent
        flags: the set of key event flags

    Returns:
        Union['WebDriver', 'Keyboard']: Self instance
    """
    data = {
        'keycode': keycode,
    }
    if metastate is not None:
        data['metastate'] = metastate
    if flags is not None:
        data['flags'] = flags
    self.execute(Command.PRESS_KEYCODE, data)
    return self

作用

发送按键code,模拟手机按键【Android专属】

参数讲解

  • keycode:按键代码
  • metastate:键码的元信息
  • flags:按键事件的合集

用法

如输入大写字母A:按下caps_lock大小写锁定键输入字符 press_keycode(29,115) 输入大写A

driver.press_keycode(29,115)

按下返回键:

driver.press_keycode(4)

driver.long_press_keycode()

源码:

def long_press_keycode(self: T, keycode: int, metastate: Optional[int] = None, flags: Optional[int] = None) -> T:
    """Sends a long press of keycode to the device.

    Android only. Possible keycodes can be found in
    http://developer.android.com/reference/android/view/KeyEvent.html.

    Args:
        keycode: the keycode to be sent to the device
        metastate: meta information about the keycode being sent
        flags: the set of key event flags

    Returns:
        Union['WebDriver', 'Keyboard']: Self instance
    """
    data = {'keycode': keycode}
    if metastate is not None:
        data['metastate'] = metastate
    if flags is not None:
        data['flags'] = flags
    self.execute(Command.LONG_PRESS_KEYCODE, data)
    return self

作用

按键code长按发送给设备,模拟手机按键【Android专属】

参数讲解

  • keycode:按键代码
  • metastate:键码的元信息
  • flags:目标按键事件

用法

driver.long_press_keycode(4)

driver.hide_keyboard()

源码:

def hide_keyboard(
    self: T, key_name: Optional[str] = None, key: Optional[str] = None, strategy: Optional[str] = None
) -> T:
    """Hides the software keyboard on the device.

    In iOS, use `key_name` to press
    a particular key, or `strategy`. In Android, no parameters are used.

    Args:
        key_name: key to press
        key:
        strategy: strategy for closing the keyboard (e.g., `tapOutside`)

    Returns:
        Union['WebDriver', 'Keyboard']: Self instance
    """
    data: Dict[str, Optional[str]] = {}
    if key_name is not None:
        data['keyName'] = key_name
    elif key is not None:
        data['key'] = key
    elif strategy is None:
        strategy = 'tapOutside'
    data['strategy'] = strategy
    self.execute(Command.HIDE_KEYBOARD, data)
    return self

作用

隐藏设备上的键盘

参数讲解(参数仅适用iOS)

  • key_name:iOS 才需要此参数
  • key:未知
  • strategy:隐藏键盘的模式

用法

driver.hide_keyboard()

driver.keyevent()

源码:

def keyevent(self: T, keycode: int, metastate: Optional[int] = None) -> T:
    """Sends a keycode to the device.

    Android only.
    Possible keycodes can be found in http://developer.android.com/reference/android/view/KeyEvent.html.

    Args:
        keycode: the keycode to be sent to the device
        metastate: meta information about the keycode being sent

    Returns:
        Union['WebDriver', 'Keyboard']: Self instance
    """
    data = {
        'keycode': keycode,
    }
    if metastate is not None:
        data['metastate'] = metastate
    self.execute(Command.KEY_EVENT, data)
    return self

作用

发送按键code,模拟手机按键【Android专属】

参数讲解

  • keycode:按键代码
  • metastate:键码的元信息

注意

现在这方法其实已经过时了,使用press_keycode取代它,否则可能会错误

用法

driver.keyevent(4)

driver.is_keyboard_shown()

源码:

def is_keyboard_shown(self: T) -> bool:
    """Attempts to detect whether a software keyboard is present

    Returns:
        `True` if keyboard is shown
    """
    return self.execute(Command.IS_KEYBOARD_SHOWN)['value']

作用

检查当前键盘是否已出现

返回

如果键盘已出现,则返回True

用法

print(driver.is_keyboard_shown())
EventCode KeyEvent EventName
0 KEYCODE_UNKNOWN 未知键
1 KEYCODE_SOFT_LEFT 左键
2 KEYCODE_SOFT_RIGHT 右键
3 KEYCODE_HOME Home键
4 KEYCODE_BACK 返回键
5 KEYCODE_CALL 拨号键
6 KEYCODE_ENDCALL 挂机键
7 KEYCODE_0 按键“0”
8 KEYCODE_1 按键“1”
9 KEYCODE_2 按键“2”
10 KEYCODE_3 按键“3”
11 KEYCODE_4 按键“4”
12 KEYCODE_5 按键“5”
13 KEYCODE_6 按键“6”
14 KEYCODE_7 按键“7”
15 KEYCODE_8 按键“8”
16 KEYCODE_9 按键“9”
17 KEYCODE_STAR 按键“*”
18 KEYCODE_POUND 按键“#”
19 KEYCODE_DPAD_UP 导航键 向上
20 KEYCODE_DPAD_DOWN 导航键 向下
21 KEYCODE_DPAD_LEFT 导航键 向左
22 KEYCODE_DPAD_RIGHT 导航键 向右
23 KEYCODE_DPAD_CENTER 导航键 确定
24 KEYCODE_VOLUME_UP 音量键加
25 KEYCODE_VOLUME_DOWN 音量键减
26 KEYCODE_POWER 电源键
27 KEYCODE_CAMERA 相机键
28 KEYCODE_CLEAR 清除键
29 KEYCODE_A 按键“A”
30 KEYCODE_B 按键“B”
31 KEYCODE_C 按键“C”
32 KEYCODE_D 按键“D”
33 KEYCODE_E 按键“E”
34 KEYCODE_F 按键“F”
35 KEYCODE_G 按键“G”
36 KEYCODE_H 按键“H”
37 KEYCODE_I 按键“I”
38 KEYCODE_J 按键“J”
39 KEYCODE_K 按键“K”
40 KEYCODE_L 按键“L”
41 KEYCODE_M 按键“M”
42 KEYCODE_N 按键“N”
43 KEYCODE_O 按键“O”
44 KEYCODE_P 按键“P”
45 KEYCODE_Q 按键“Q”
46 KEYCODE_R 按键“R”
47 KEYCODE_S 按键“S”
48 KEYCODE_T 按键“T”
49 KEYCODE_U 按键“U”
50 KEYCODE_V 按键“V”
51 KEYCODE_W 按键“W”
52 KEYCODE_X 按键“X”
53 KEYCODE_Y 按键“Y”
54 KEYCODE_Z 按键“Z”
55 KEYCODE_COMMA 按键“,”
56 KEYCODE_PERIOD 按键‘.’
57 KEYCODE_ALT_LEFT 组合键 Alt+Left
58 KEYCODE_ALT_RIGHT 组合键 Alt+Right
59 KEYCODE_SHIFT_LEFT 组合键 Shift+Left
60 KEYCODE_SHIFT_RIGHT 组合键 Shift+Left
61 KEYCODE_TAB Tab键
62 KEYCODE_SPACE 空格键
63 KEYCODE_SYM 选择输入法
64 KEYCODE_EXPLORER 浏览器
65 KEYCODE_ENVELOPE 邮件
66 KEYCODE_ENTER 回车键
67 KEYCODE_DEL 退格键
68 KEYCODE_GRAVE 按键‘`’
69 KEYCODE_MINUS 按键‘-’
70 KEYCODE_EQUALS 按键‘=’
71 KEYCODE_LEFT_BRACKET 按键‘[’
72 KEYCODE_RIGHT_BRACKET 按键‘]’
73 KEYCODE_BACKSLASH 按键‘\’
74 KEYCODE_SEMICOLON 按键‘,’
75 KEYCODE_APOSTROPHE 按键‘'’(单引号)
76 KEYCODE_SLASH 按键‘/’
77 KEYCODE_AT 按键‘@’
78 KEYCODE_NUM
79 KEYCODE_HEADSETHOOK
80 KEYCODE_FOCUS
81 KEYCODE_PLUS 按键‘+’
82 KEYCODE_MENU 菜单键
83 KEYCODE_NOTIFICATION
84 KEYCODE_SEARCH
85 TAG_LAST_KEYCODE

Android键盘键名和键值对应文章推荐

toast处理

  1. appium server 1.6.3 以上
  2. 代码中必须制定 automationName为:UiAutomator2, 1.14之后的 appium 可以不加
  3. UIAutomator2只支持安卓版本 5.0+
  4. jdk1.8 64位以上。配置 JAVA_HOME 环境变量;
  5. toast只有文本,只支持xpath定位, //*[contains(@text,“错误的账号信息”) 不能够用等待元素可见,只能用元素存在否则会报错
# Toast 弹框
# 方法一:直接通过文本属性定位
# driver.find_element('xpath', "//*[@text='错误的账号信息')]")
toast = driver.find_element('xpath', "//*[contains(@text,'错误的账号信息')]")
# 方法二://android.widget.Toast
# toast = driver.find_element('xpath', "//android.widget.Toast")
print(toast.text)

应用切换 driver.start_activity(包名,应用名)

# 传包名和应用名
driver.start_activity('com.tal.kaoyan', '.ui.activity.SplashActivity')