9.3 常见脚本逻辑与代码示例

1. 代码示例

Airtest脚本的示例代码,可以在 这里 找到。其中的 test_blackjack.air 为一个正常的.air脚本,大家可以下载之后在AirtestIDE中打开它查看。同样地,在 pure_python_example 中存放的是普通的.py脚本(内容与.air脚本中的同名.py文件相同),为了说明一个airtest脚本其实也是一个普通的.py文件,只是在其中引入了 airtest 这一python第三方库。

Poco的示例demo与代码,请在 这里 下载,安装到手机后可以尝试查看UI树以及熟悉API用法。

我们在Github上也有一个 Example 仓库,里面放置了一些在本教程中出现过的代码示例,大家也可以参考。

2. 如何连接手机/windows窗口

根据 运行脚本 的方式不同,连接手机的方式也略有区别,例如当使用 airtest run test.air 这种方式运行脚本时,只需要根据文档内容的范例,在命令行里加上 --device 设备连接字符串 即可。关于设备字符串如何填写,请查阅 文档 ,此处是简单示例:

>airtest run untitled.air --device Android:///手机设备号 --log log/
>python -m airtest run untitled.air --device Android:///手机设备号 --log log/

如果是在 .py 脚本文件中 import airtest/poco ,希望以普通python脚本的方式来运行的话,可以在脚本代码中使用 connect_device 接口:

from airtest.core.api import *
connect_device("Android:///")  # 同样是设备字符串

这两种连接方式,只需要 选择其中一种 即可,如果都使用了,很可能造成重复连接等其他问题。

如果设备字符串 Android:/// 不太会编写的话,可以直接先用AirtestIDE连上一次设备后,复制IDE里运行脚本时打印出来的 --device Android:/// 的字符串内容使用,这样可以简单方便地连上设备。

3. keyevent按键码

在脚本中,有时需要输入一些指定的按键,例如点一下HOME键、BACK键等,如果设备是Android设备,可以参考谷歌的 Android按键码 (国内用户如果打不开此链接,可直接以关键词:Android keyevent 搜索)。

示例: keyevent("KEYCODE_DEL") 或者直接输入按键码,注意参数为字符串: keyevent("67")

在Windows系统中,请参考 pywinauto 这个库提供的 Windows按键码

示例: keyevent("{BACKSPACE}")

iOS设备现在暂时 只支持 HOME 按键的keyevent。

4. 如何输入文字

Airtest中的文字输入接口text

在脚本中,如果想要实现文字输入,一般需要这样的流程:

  1. 点一下你需要输入的位置,激活输入光标
  2. 调用airtest的 text() 接口来输入内容
../_images/api_text.png

如图,在这个脚本里 先点击了需要输入的位置 ,然后调用了text接口进行输入,在运行时手机将会自动被安装一个名为 yosemite.apk 的应用,然后启用 yosemite输入法 来进行输入。

而且需要注意的是,在使用过text接口后,手机输入法会被切换为yosemite输入法,因此看不到正常键盘了(无需惊慌)。如果需要手工输入,可以在系统的输入法设置中,把输入法切换回系统输入法即可恢复。同时我们也提供了 安卓手机助手功能 ,在手机助手中可以简单地点击鼠标来切换输入法。

输入失败的处理

如果 text() 接口输入失败,请查看是否手机阻止了该apk的安装及运行,或是部分手机的兼容性问题导致的,具体可以查阅 Android连接FAQ ,以及部分型号的手机不允许输入密码时调用第三方输入法,请查阅FAQ文档对手机设置进行修改。

同时可以尝试将 yosemite输入法 设置为手机默认输入法,然后再进行 text() 接口的调用。

部分型号手机在使用 text() 接口 输入密码 时,会输入失败,这是因为手机设置中的 语言与输入法-安全输入 没有打开,打开该选项后就可以使用非系统自带的输入法来输入密码了。

有部分特殊型号的手机,可能在使用Yosemite输入法时容易失败,无法输入文字(OPPO与Vivo品牌更容易出现),假如没有输入中文的需求,可以尝试使用 adb shell input 指令来进行文字输入:

shell("input text 'hello world'")

同时,上面这种 adb shell input 可以直接设置为默认的输入方式,替换原先的Yosemite输入,例如这样在python代码中这样初始化手机:

from airtest.core.api import *
# 相当于命令行中使用 --device Android:///?ime_method=ADBIME 连接手机
init_device("Android", ime_method="ADBIME")
text("hello")

部分模拟器(例如夜神模拟器)在输入时可能无法成功,可以尝试确认设置中是否打开了 硬件-物理键盘,尝试关闭这个选项,并设置默认输入法为yosemite输入法后再次重试即可。具体设置方式请参考 部分模拟器相关问题

输入完毕后的回车与搜索键

text接口有一个默认参数 enter=True,会在输入完毕后自动按一下回车键(相当于keyevent(“ENTER”)),假如不需要,请传入 enter=False 。部分输入框,需要在输入内容后,点击输入键盘上的 搜索 按钮才能够激活搜索操作,可以传入 search=True 参数:

text("test", search=True)

在这里,回车键的keyevent(“ENTER”)与输入法中的搜索、换行等操作是不同的,输入法中显示的额外按键是 EDITOR CODE,刚才这个代码中的 search=True 实际上是传入了editor code 3。因为搜索键是最常用的按键,因此我们将它封装进了 text 接口中,如果有点击除了 搜索 以外其他按钮的需求,需要查阅文档 Editor Action Code 来获取代码(如果该网页访问不了的话,可以自行搜索关键词 IME_ACTION_SEARCH ),然后手动传入代码进行点击:

dev = device()
# 按一下输入法的Go按钮 IME_ACTION_GO,对应键值为2,同理,输入法的搜索键实际上对应值为3
dev.yosemite_ime.code("2")
# 上面代码等价于下面这个shell调用
# shell("am broadcast -a ADB_EDITOR_CODE --ei code 2")

更多代码,可以查询 Editor Action Code 来获取,如果该网页访问不了的话,可以自行搜索关键词 IME_ACTION_SEARCH

Poco中的set_text

除了airtest的text接口之外,我们在poco中也提供了一个 set_text 接口,这个接口无需调用输入法,可以直接设置文字。但是指定的控件必须是一个可输入的控件,例如在Android中,是一个 EditText 类型的widget:

poco("com.android.mms:id/recipients_editor").set_text("test")

我们建议各位如果项目已经接入了poco,可以多尝试使用poco的set_text来设置文字内容,如果无法输入(部分机型、部分输入框有可能不支持set_text接口),再尝试用airtest的text接口。

5. 如何删除输入框中的内容

目前,删除输入框的内容可以通过 keyevent 接口来完成,参考本章的第一节 3. keyevent按键码 ,很容易可以写出本平台对应的删除操作,例如安卓平台下的两种写法:

keyevent("KEYCODE_DEL")
keyevent("67")  # 67即为删除键,请注意传入的是字符串

airtest模拟的是用户的操作,用户在删除一个输入框内容时,会连续N次地去点击删除按钮,因此在airtest脚本中想要实现“清空输入框”内容的操作,也需要写一个循环连续运行N次keyevent操作:

for i in range(10):
    keyevent("67")

不过,如果已经接入了Poco,可以考虑直接使用Poco的set_text接口,将输入框内容设置为空字符串即可: poco("xxx").set_text("")

6. airtest脚本中适当添加等待时间sleep

在脚本编写过程中,如果有连续点击操作,屏幕内容可能会不断变化,有时候会导致脚本明明运行到了点击操作却发现没有生效的情况。这是因为屏幕内容切换速度过快,界面还未稳定的同时airtest就进行了元素识别和操作,导致没有成功点击到对应元素。

因此我们通常建议,在一些操作步骤结束后,适当等待一个合适的时间再进行下一步操作,例如:

from airtest.core.api import *
start_app("test_package")
sleep(5)
touch([500, 500])
sleep(1)
touch([600, 0])

如果希望在每一步操作后都增加一个固定的统一等待时间,我们提供了一个名为 OPDELAY 的全局变量,设置它可以修改airtest操作之间的间隔,可以参考 Airtest脚本相关配置 中的 OPDELAY 相关介绍。

7. 如何引用别的.air脚本中封装好的方法

如果想要在一个.air脚本中,调用另外一个.air脚本里封装的公用函数,可以这样做:

from airtest.core.api import using
# 相对路径或绝对路径,确保代码能够找得到即可
using("common.air")

from common import common_function
common_function()

如果需要引用的子脚本路径统一都放在某个目录下,可以通过设定一个默认项目根目录 PROJECT_ROOT ,让使用 using 接口时能够在当前根目录下寻找别的子脚本,无需填写完整路径,让脚本之间相互调用使用更加方便。

例如,我们建立一个名为test1.air的脚本,实际路径为 /User/test/project/test1.air

from airtest.core.api import *

def test():
    touch("tmp.png")

在同一目录下另外一个main.air脚本可以这样引用到它里面的 test

from airtest.core.api import *

ST.PROJECT_ROOT = "/User/test/project"
using("test1.air")
from test1 import test

8. 连续滑动与自定义点击滑动操作

有些时候在 安卓 手机上面,我们需要实现连续滑动多个点的操作(例如屏幕滑动图案解锁功能),我们提供了一个 swipe_along 操作,示例代码如下:

from airtest.core.api import *
dev = device()  # 获取当前手机设备
# 手指按照顺序依次滑过3个坐标
dev.minitouch.swipe_along([(100, 100), (200, 200), (300, 300)])

关于手机屏幕坐标的获取,可以参考 IDE设置-Device 打开 Show Real-time Cursor Coordinate 选项后即可实时在手机画面上看到坐标信息,右键点击鼠标还能将坐标复制到剪贴板中,这样可以很方便地实现一些在坐标间滑动的需求,如图:

../_images/swipe_along.gif

请注意这个接口目前只有在使用了默认的 minitouch 模式时才能使用,同时我们还提供了一种自定义点击或滑动操作的方案,可以供大家实现更加符合需求的操作:

from airtest.core.api import *
from airtest.core.android.minitouch import *

connect_device("Android:///")

# 实现两个手指同时点击的操作
multitouch_event = [
    DownEvent((100, 100), 0),  # 手指1按下(100, 100)
    DownEvent((200, 200), 1),  # 手指2按下(200, 200)
    SleepEvent(1),
    UpEvent(0), UpEvent(1)]  # 2个手指分别抬起

device().minitouch.perform(multitouch_event)

以上代码中使用的接口由于属于底层操作,因此使用的坐标系为设备的原始坐标,没有经过横竖屏的坐标转换(默认为竖屏)。若需要对横屏进行操作,可以自行进行坐标转换,具体的代码实例地址在 这里