6.3 脚本撰写的高级特性

在编写测试脚本的过程中,经过airtest封装的简单语句可能满足不了更复杂的代码编写需求,这一章主要介绍脚本编写中的一些高级特性。

Airtest脚本目前支持的自定义功能有:

  • 设置脚本运行过程中的参数
  • 添加自定义方法,或者替换默认方法
  • 在测试用例的运行前后,执行初始化代码或清理代码,甚至是调用其他用例脚本

6.3.1 启动器介绍

Airtest在运行用例脚本时,在继承unittest.TestCase的基础上,实现了一个叫做AirtestCase的类,添加了所有执行基础Airtest脚本的相关功能。

因此,假如需要添加自定义功能,只需要在AirtestCase类的基础上,往setup和teardown中加入自己的代码即可。如果这些设置和功能内容相对固定,可以将这些内容作为一个launcher,用来在运行实际测试用例之前初始化相关的自定义环境。

本部分详情介绍可参考: Airtest帮助文档的相关内容.

6.3.2 启动器范例

以下是一个自定义custom_launcher.py的范例代码:

from airtest.cli.runner import AirtestCase, run_script
from airtest.cli.parser import runner_parser


class CustomAirtestCase(AirtestCase):

    def setUp(self):
        print("custom setup")
        # add var/function/class/.. to globals
        # self.scope["hunter"] = "i am hunter"
        # self.scope["add"] = lambda x: x+1

        # exec setup script
        # self.exec_other_script("setup.owl")
        super(CustomAirtestCase, self).setUp()

    def tearDown(self):
        print("custom tearDown")
        # exec teardown script
        # self.exec_other_script("teardown.owl")
        super(CustomAirtestCase, self).setUp()


if __name__ == '__main__':
    ap = runner_parser()
    args = ap.parse_args()
    run_script(args, CustomAirtestCase)

它的主要功能就是:在AirtestCase的基础上,添加了一些自定义的内容,同时可以通过它作为一个启动器,来运行实际的测试用例代码。

例如:

python custom_launcher.py test.air --device Android:///serial_num --log log_path

这个命令行使用自定义的启动器custom_launcher.py来运行名为test.air的脚本。

6.3.3 Airtest-IDE中的启动器

在IDE中,运行脚本的方式有两种,第一种是简单地在编辑框里写上要运行的Airtest代码,之后就能够用默认方式来运行脚本,无需其他额外的代码。

第二种,就是在刚才介绍的启动器基础上,实现一个自定义的启动器,这样能够初始化一个自定义的环境,供后来编写的Airtest代码运行。

配置方法:

菜单-“选项”-“设置”-“Airtest”,点击“自定义启动器”可打开文件选择窗口,选择自定义的launcher.py文件即可。

点击“编辑”,可对launcher.py文件的内容进行编辑,点击“确定”按钮让新配置生效。

代码范例:

以下是一份 在AirtestIDE中 使用的启动器代码范例(与刚才那份命令行可直接运行的 `6.1.2 启动器范例`_ 相比,IDE下可以把当前运行的代码行标出颜色,以及未来可能还可以额外支持其他功能):

from airtest.cli.runner import run_script
from airtest.cli.parser import runner_parser
from airtest.core.settings import Settings as ST
# 假如脚本在IDE中运行,IDE会自动帮忙加载AirtestCase。假如要用命令行独立运行脚本,则需要手工import AirtestCase
if not global().get("AirtestCase"):
    from airtest.cli.runner import AirtestCase

class CustomAirtestCase(AirtestCase):
    def __init__(self):
        super(CustomAirtestCase, self).__init__()

    def setUp(self):
        print("custom setup")
        super(CustomAirtestCase, self).setUp()

    def tearDown(self):
        print("custom tearDown")
        super(CustomAirtestCase, self).tearDown()

if __name__ == '__main__':
    ap = runner_parser()
    args = ap.parse_args()
    run_script(args, CustomAirtestCase)

6.3.4 启动器中自定义内容介绍

在自定义的启动器代码中,继承AirtestCase之后,能够在setUp()与tearDown()方法里对执行脚本时的环境变量进行设置:

class CustomAirtestCase(AirtestCase):
    def setUp(self):
        # add var/function/class/.. to globals
        super(CustomAirtestCase, self).setUp()

1. 添加自定义变量与方法:

将自定义变量添加到self.scope里,之后用该启动器来运行Airtest的脚本,脚本代码中就能够直接使用这些变量,无需重新定义:

def setUp(self):
    self.scope["hunter"] = "i am hunter"
    self.scope["add"] = lambda x: x+1

2. 在正式脚本运行前后,添加子脚本的运行:

在执行测试用例时,有一类很常见的需求,是在正式用例之前执行固定的初始化脚本,或是在用例执行完毕后执行清理脚本.

在自定义Airtest启动器中,可以使用很简单的代码来完成这类需求:

class CustomAirtestCase(AirtestCase):
    PROJECT_ROOT = "子脚本存放公共路径"  # 可以将子脚本放置在某个公共目录下

    def setUp(self):
        # exec setup script
        self.exec_other_script("setup.air")  # 假设该setup.air脚本存放在PROJECT_ROOT目录下,调用时无需填写绝对路径,可以直接写相对路径
        super(CustomAirtestCase, self).setUp()

    def tearDown(self):
        # exec teardown script
        self.exec_other_script("teardown.air")
        super(CustomAirtestCase, self).setUp()

3. 在正式脚本里,添加子脚本的运行:

另一类常见需求是在脚本内执行子脚本,同样可以将子脚本存放在PROJECT_ROOT公共目录下,然后在脚本test.air中调用代码:

exec_script("子脚本.air")

4. 修改Airtest默认参数值:

Airtest在运行脚本和识别图像的过程中,由于默认配置的参数值可能不符合实际运行需要,以下代码演示了如何修改默认参数配置:

from airtest.core.settings import Settings as ST

class CustomAirtestCase(AirtestCase):
    def setUp(self):
        ST.THRESHOLD = 0.75

上面这段代码,将默认配置的图像识别准确率阈值改为了0.75(默认值是0.6),降低了图像识别的错误率。

在airtest.core.settings的Settings中,有以下配置选项可以进行修改:

THRESHOLD 图像识别精确度阈值,0~1之间,越大表示识别精度越高
THRESHOLD_STRICT assert语句里图像识别时使用的高要求阈值,默认为0.7
RESIZE_METHOD 可以对图像识别时的UI跨分辨率规律进行重定义
LOG_DIR 脚本运行log存放路径,默认为脚本当前路径
LOG_FILE 运行log的存放文件名,默认为log.txt
OPDELAY 每一步操作后等待多长时间进行下一步操作,默认0.1s
FIND_TIMEOUT 进行图像查找时的超时时间,默认为20s

5. 注册自定义设备,实现更多复杂操作:

在Airtest中,默认支持三种设备:Android, Windows和IOS,在执行脚本的命令行中通过参数 –device Android:/// 来指定脚本运行在某一类型设备上。

假如需要对这些设备的某些运行操作进行修改,可以自行实现一种新的设备,并将它注册到Airtest中,即可在运行脚本的时候调用了。

例如,当IDE中的Airtest脚本运行在Windows模式下时,由于截图大小的问题需要对Airtest的Windows代码进行一些修改。因此IDE实现了一个新的类,名为WindowsInIDE:

from airtest.core.win.win import Windows, screenshot
class WindowsInIDE(Windows):
    def snapshot(self, filename="tmp.png"):
        # 实现了IDE相关的特殊截图操作
        pass

然后,在启动器中将这个WindowsInIDE注册到Airtest中:

class AirtestIDECase(AirtestCase):
"""单独在IDE中使用,添加了windows相关的特殊处理"""
def __init__(self):
    if get_platform() == "Windows":
        import WindowsInIDE
        # 注册一种ide专属的windows模式设备到airtest中
        from airtest.core.api import G
        G.register_custom_device(WindowsInIDE)
    super(AirtestIDECase, self).__init__()

注册后,在执行脚本时,修改运行指令中的device参数,就能使用刚才注册的新设备名称来运行脚本了:

python launcher.py test.air --device WindowsInIDE:///

6.3.5 总结

自定义启动器可以进行配置项的修改和实现更多复杂需求,类似于unittest,能够将原先的Airtest脚本作为测试用例来运行。

在IDE中,若想实现一个自定义启动器的步骤如下:

  1. 新建一个custom_launcher.py文件,在里面实现一个继承了AirtestCase的类。
  2. 在setUp()和tearDown()中,添加上自己想要实现的操作。
  3. 在IDE的选项-配置-Airtest配置中,将刚才实现的custom_launcher.py设置为启动器。
  4. 直接点击运行脚本即可。或者使用命令行的方式运行:python custom_launcher.py test.air airtest脚本参数