概述
本次是分析airtest的运行过程,通过分析我们将知道airtest是怎么运行.air文件脚本的
入口
关键代码有两个地方,一个是main文件,作为接收命令行参数,另一个是/core/cli/runner.py文件里面
先来看看main.py文件
def main(argv=None):
ap = get_parser()
args = ap.parse_args(argv)
if args.action == "info":
from airtest.cli.info import get_script_info
print(get_script_info(args.script))
elif args.action == "report":
from airtest.report.report import main as report_main
report_main(args)
elif args.action == "run":
from airtest.cli.runner import run_script
run_script(args)
elif args.action == "version":
from airtest.utils.version import show_version
show_version()
else:
ap.print_help()
这个比较简单,就是处理传参的,参考官方文档我们一般执行脚本是通过:
airtest run untitled.air --device Android:///手机设备号 --log log/
所以执行脚本是通过run参数,run参数再调用runner.py文件的run_script方法,因此呢我们可以自定义执行脚本,只需要接收参数并调用run_script方法即可,此处官方文档已经给出了自定义启动器的教程,我们已经知道原理了,直接照着官方文档走就行了,传送门
core/cli/runner.py
接下来我们就开始具体分析这个文件
打开这个方法
def run_script(parsed_args, testcase_cls=AirtestCase):
global args # make it global deliberately to be used in AirtestCase & test scripts
args = parsed_args
suite = unittest.TestSuite()
suite.addTest(testcase_cls())
result = unittest.TextTestRunner(verbosity=0).run(suite)
if not result.wasSuccessful():
sys.exit(-1)
嗯,有种熟悉感,本质就是unitest框架,把testcase装进suite,然后定义runner执行即可,这里没办法解释是怎么运行.air的,所以线索就在默认参数AirtestCase这个类里面,这个同样定义在runner.py文件中
AirtestCase
最上面有几个初始化的test fixture方法setUpClass、setUp、tearDown,基本作用就是初始化设备参数,
@classmethod
def setUpClass(cls):
cls.args = args
setup_by_args(args)
# setup script exec scope
cls.scope = copy(globals())
cls.scope["exec_script"] = cls.exec_other_script
def setUp(self):
if self.args.log and self.args.recording:
for dev in G.DEVICE_LIST:
try:
dev.start_recording()
except:
traceback.print_exc()
def tearDown(self):
if self.args.log and self.args.recording:
for k, dev in enumerate(G.DEVICE_LIST):
try:
output = os.path.join(self.args.log, "recording_%d.mp4" % k)
dev.stop_recording(output)
except:
traceback.print_exc()
这几个方法基本就是初始化参数的作用了,包括设备参数,安卓端的视频录制参数这些,核心的方法还是runTest方法,这也是unitest框架的测试执行方法
def runTest(self):
scriptpath, pyfilename = script_dir_name(self.args.script)
pyfilepath = os.path.join(scriptpath, pyfilename)
pyfilepath = os.path.abspath(pyfilepath)
self.scope["__file__"] = pyfilepath
with open(pyfilepath, 'r', encoding="utf8") as f:
code = f.read()
pyfilepath = pyfilepath.encode(sys.getfilesystemencoding())
try:
exec(compile(code.encode("utf-8"), pyfilepath, 'exec'), self.scope)
except Exception as err:
tb = traceback.format_exc()
log("Final Error", tb)
six.reraise(*sys.exc_info())
主要有两个作用:
- 解析传入的xx.air的路径和实际.py
- 通过exec执行.py文件,运行脚本
那么分析到这里也就明白了执行airtest run xxx.air实际上是exec执行了.py文件