ATX 基于 ATX-Server 的 UI 自动化测试框架

linpengcheng · June 18, 2018 · Last by linpengcheng replied at August 20, 2019 · 9865 hits

分享一个基于ATX-Server的UI自动化测试框架,可以实现多设备的并行测试,并生成统一的测试报告GitHub地址:https://github.com/pengchenglin/ATX-Test

前置条件

Android设备需要通过uiautomator2 init 初始化完成,确认可以正常连接 ,或者init 接入atx-server
相关的基础链接如下
小白入门篇:python uiautomator2 的代码示例
浅谈自动化测试工具 python-uiautomator2
atx 安卓集群管理 安装运行及自动化的实践
ATX-uiautomator2 实现 webview 的操作

先行声明:

1.下面展示的内容多源于TesterHome各位前辈的经验总结,我只是按照个人想法进行了简单拼接(基于ATX-uiautomator2的Android自动化测试)
2.主要参考了@hualin (王华林) 老师的https://testerhome.com/topics/7550uiautomator2实现,并在此基础上结合
3.所用语言为Python,测试报告模板借用了https://github.com/Gelomen/HTMLTestReportCN-ScreenShot,并进行了简单的修改以方便截图
4.使用了macaca的bootstrap app作为demo演示

工程介绍

工程目录如下

主体结构和@hualin (王华林) 老师的https://testerhome.com/topics/7550 的一致,主要修改了Pubilc下一些东西,并增加了一些东西

Public:

  • ATX-Server.py 获取atx-server上特定设备、或config.ini下devices列表的在线设备
  • Devices.py 获取atx-server上特定设备(ATX_Server(object))、或config.ini下devices IP列表的在线设备(get_devices())、有线连接电脑的设备自动连接u2(connect_devices())
  • BasePage.py 用于设备的初始化 u2.connect 以及一些公共模块的封装
  • chromedriver.py 和Ports.py 结合使用,启动chromedriver以便实现u2的webview操作(目前还没做到根据设备的chromeversion 启动指定版本的chromedriver)
  • Casestrategy.py 获取指定路径下的testcases
  • Decorator.py 有@testcase@teststep这样的装饰器用例执行日志打印、错误后的处理(截图)
  • Report.py 对生成的报告的一些操作,备份Testreport的报告到TestReport_backup下、多设备统一报告的生成、报告的文件夹压缩
  • Test_data.py 在执行测试前的测试数据的生成,会在Plubic下生成data.json,测试执行的时候各个设设备根据自己的serial获取对应的测试数据
  • Drivers.py 设备的获取,初始化准备,测试执行都是在这里完成的
  • RunCases.py 存放测试报告/日志/截图的路径的生成,以及最终通过HTMLTestRunner来执行用例
  • config.ini 一些需要用到的数据,tatx-server地址、测试设备的ip、测试数据等

下面介绍一下流程:
1、通过run_cases .py或者run_all_cases.py开始执行测试

if __name__ == '__main__':
# back up old report dir 备份旧的测试报告文件夹到TestReport_backup
backup_report()

cs = CaseStrategy()
cases = cs.collect_cases(suite=False)
Drivers().run(cases)

# Generate zip_report file 压缩测试报告文件
# zip_report()

​ 1.首先会将Testreport目录剪切到TestReport_backup目录下,备份旧的测试报告
​ 2.通过CaseStrategy获取到需要执的测试用例
​ 3.Drivers().run(cases)开始执行测试
​ 4.执行完成之后打包压缩,没啥用 注释掉了

2、run(cases)执行测试

def run(self, cases):
# 根据method 获取android设备
method = ReadConfig().get_method().strip()
if method == 'SERVER':
# get ATX-Server Online devices
devices = ATX_Server(ReadConfig().get_server_url()).online_devices()
print('\nThere has %s online devices in ATX-Server' % len(devices))
elif method == 'IP':
# get devices from config devices list
devices = get_devices()
print('\nThere has %s devices alive in config IP list' % len(devices))
elif method == 'USB':
# get devices connected PC with USB
devices = connect_devices()
print('\nThere has %s USB devices alive ' % len(devices))

else:
raise Exception('Config.ini method illegal:method =%s' % method)

if not devices:
print('There is no device found')
return

# generate test data data.json 准备测试数据
generate_test_data(devices)

print('Starting Run test >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
runs = []
for i in range(len(devices)):
runs.append(RunCases(devices[i]))

# run on every device 开始执行测试
pool = Pool(processes=len(runs))
for run in runs:
pool.apply_async(self._run_cases,
args=(run, cases,))
print('Waiting for all runs done........ ')
pool.close()
pool.join()
print('All runs done........ ')
ChromeDriver.kill()

# Generate statistics report 生成统计测试报告 将所有设备的报告在一个HTML中展示
create_statistics_report(runs)

​ 1.首先根据config.ini中method的值来判断从atx-serve获取online的设备 还是从config.ini中的ip来获取在线的设备,或者直接获取连接电脑的安卓设备
​ 2.在获取到设备之后,根据设备生产data.json测试数据
​ 3.并行多设备执行测试
​ 4.测试完之后,杀掉执行过程中打开的所有的chromedriver进程
​ 5.最后在TestReport下生成统计测试报告(自动化测试报告.html)

结果展示

生成的测试报告路径结构如下

每个设备的测试结果及报告或存放在单独的文件夹下
在Testreport目录下会有一个统计测试报告(自动化测试报告.html)会将所有设备的报告统一在一个页面展示
报告展示:

附言 1  ·  June 18, 2018

需要依赖的python第三方库 tinydbuiautomator2seleniumjinja2

附言 2  ·  June 19, 2018

还有个python第三方库 psutil

附言 3  ·  June 24, 2018

method 设置为USB之后,手机有线连接到电脑的,就算offline的设备也能自动重启u2跑脚本了。
前提是你的u2要升级到 Version: 0.1.3.dev5 及以后,guthub上更新最新的ATX-Test 代码
因为uiautomator2 更新了connect_usb方法
def connect_usb(serial=None):
"""
Args:
serial (str): android device serial
"""
adb = adbutils.Adb(serial)
lport = adb.forward_port(7912)
device = connect_wifi('127.0.0.1:' + str(lport))
if not device.alive:
warnings.warn("atx-agent is not alive, start again ...", RuntimeWarning)
adb.execute("shell", "/data/local/tmp/atx-agent", "-d")
device.healthcheck()
return device

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 66 条回复 时间 点赞

开源大赞

来学习下

不错不错

学习~学习~😀

先star了

学习~学习~

和appium结合stf的思想差不多

uiatuomator2 实现了toast的获取 请更新u2到最新的 0.1.3.dev2
https://github.com/openatx/uiautomator2#toast

然而
d.watchers.watched = True有问题 可能在0.1.3.dev3中解决

9Floor has been deleted

在 CaseStrategy 里的 suite_path 和case_path ,case_pattern 路径应该要怎样配置?配置全路径吗?
我运行时报了以下错

Traceback (most recent call last):
File "/my_work/my_work/ATX-Test-master/run_all_cases.py", line 16, in <module>
cases = cs.collect_cases(suite=True)
File "/my_work/my_work/ATX-Test-master/Public/CaseStrategy.py", line 40, in collect_cases
self._collect_cases(cases, top_dir=test_suite)
File "/my_work/my_work/ATX-Test-master/Public/CaseStrategy.py", line 16, in _collect_cases
pattern=self.case_pattern, top_level_dir=top_dir)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/loader.py", line 304, in discover
os.path.dirname((the_module.__file__)))
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/posixpath.py", line 156, in dirname
p = os.fspath(p)
TypeError: expected str, bytes or os.PathLike object, not NoneType
huan 回复

self.suite_path = 'TestSuite_'
self.case_path = 'TestCase'
self.case_pattern = 'test*.py'

文件夹和文件以上面的命名开头就行了啊

linpengcheng 回复

就是跑你的demo😅

huan 回复

TestSuite_demo 下的run_cases.py运行乐视报这个错?

linpengcheng 回复

这个没报,是run all cases 报错

huan 回复

看看和最新github上的是不是一样的 你本地的代码是什么样的

2018-06-26 10:20:40,943 - Redmi Note 4X - INFO - udid: c9c6d32a0804-38:e6:0a:7c:1b:20-Redmi_Note_4X
Error TestCase..pytest_cache (unittest.loader._FailedTest)
大神,这个错误是哪里的问题

renfenghui 回复

unittest.loader._FailedTest 用例的case代码有问题吧

Author only
renfenghui 回复

错误日志贴全一点 不知道到底哪里出的错 你自己分析分析应该也能找到出错的原因的

测试报告不错,是不是我在appium框架中也能用

枫叶 回复

可以啊 吧截图的那个改一下就可以了 我也是拿过来稍微改了下

学习了!!

Waiting for all runs done........
2018-10-31 15:34:11,248 - huawei tag-tl00 - INFO - udid: 00b84da5--huawei_tag-tl00
All runs done........
All chromedriver pid killed
Traceback (most recent call last):
File "C:/Users/diaost/Desktop/ATX-Test-master/run_all_cases.py", line 17, in
Drivers().run(cases)
File "C:\Users\diaost\Desktop\ATX-Test-master\Public\Drivers.py", line 97, in run
create_statistics_report(runs)
File "C:\Users\diaost\Desktop\ATX-Test-master\Public\Report.py", line 66, in create_statistics_report
tmp_dic.update(_get_report_info(run))
File "C:\Users\diaost\Desktop\ATX-Test-master\Public\Report.py", line 38, in _get_report_info
with open(report, 'r', encoding='utf-8') as f:
FileNotFoundError: [Errno 2] No such file or directory: './TestReport/2018-10-31_15_34_10-huawei tag-tl00/TestReport.html'
大神,运行报这个错要怎么解决的,是哪里的路径需要改吗

yideng-dst 回复

你的用例没执行成功吧TestReport.html文件都没生成
可以直接将ATX-Test-master\Public\Drivers.py", line 97 的 create_statistics_report(runs)这句话注释掉好了 其实这个只是在运行完生成一个统计的报告页面罢了

@linpengcheng 单个case如何调试, 不能用TestSuite()来调试么

梵心一点 回复

可以啊 这样就好了吧

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
sys.path.append('..')
from Public.Drivers import Drivers
from Public.Report import *
from TestSuite_demo.TestCase import test_01_install


if __name__ == '__main__':
# back up old report dir 备份旧的测试报告文件夹到TestReport_backup下
backup_report()

cases = unittest.TestSuite()
cases.addTest(test_01_install.app_install('test_01_install_apk'))

Drivers().run(cases)
linpengcheng 回复

多谢,早上来公司已经可以了

hi,
我把APP的包名改成我们公司的app,为什么启动的是debug的Leaks?

521anran 回复

?检查检查代码看

我在win10上运行的,但是无法安装apk,报Exception, -32002 Client error: <> data: , method: None错误,请帮忙看下?

andyzhouh2017 回复

oppo的安装是要输入密码才能安装的吧 直接adb install 应该会有问题

目前采用Uiautomator2工具做UI自动化测试,发现经常有些异常信息出现导致程序不能正常的执行下去,所以针对这些异常,我想采用监控app 软件是否在最上层,如果不在最上层就home键盘把后台进程都kill掉 用python实现?或者有没有更好的方法来处理?

另外,可以python实现Uiautomator2监听网络状态,连接指定Wi-Fi吗?

andyzhouh2017 回复

d.current_app()可以查看当前页面启动的包名和activity名称
d.app_stop_all()可以关闭所有的第三方安装启动着的app

运行脚本的时候$ python3 Auto_ota.py为什么总是报如下警告:
C:\Python36\lib\subprocess.py:761: ResourceWarning: subprocess 14572 is still running
ResourceWarning, source=self)
Auto_ota.py:38: ResourceWarning: unclosed file <io.BufferedReader name=3>
check_ip_ping()
C:\Python36\lib\site-packages\uiautomator2__init
_.py:375: RuntimeWarning: uiautomator2 is not reponding, restart uiautomator2 automatically
stacklevel=1)
.

andyzhouh2017 回复

Auto_ota.py试啥?

你好,请问在你的框架基础上如何单个循环执行cases 100次?

andyzhouh2017 回复

传参 加个循环???

是的,比如:run_all_case 用例 循环次数,以这种方式跑,可以做吗?

Author only
andyzhouh2017 回复

直接手机号找人就好


博主你好这里是匹配的什么呢?图片的路径吗?还有啊image = self.REPORT_TEST_OUTPUT_IMAGE % dict(
screenshot=saxutils.escape(uo + ue)这一部分不太懂

bianwancheng 回复

截图的操作会输出 htmltesterrunner读取到输出的内容 带IMAGE:字段的 解析出来 吧真实的图片地址添加到报告里

linpengcheng 回复

谢谢楼主,已经摸索出来了

linpengcheng 回复

如果我想在控制台打印出测试案例里面的日志改怎么设置呢?比如
控制台打印出print语句

49Floor has been deleted
bianwancheng 回复

直接logger.info()输出内容就好了吧

linpengcheng 回复


我用这种方式,不会打印测试案例里面的print或者Logging的东西,只打印了成功或者失败的结果。请问是怎么配置的?

bianwancheng 回复

testsuite下的csaes的py文件写法和pageobject目录下的写法一样 log.i(xxxxxxxx)就好了

楼主您好,在执行case的时候,如果多个手机里面的webview对应的chromdriver版本不一样,请问这样的该怎么处理呢?

bjxiehong 回复

获取到手机上chrome的版本 然后启动PC端指定版本的chromedriver
可以参考这个
https://testerhome.com/topics/15915

Bach · #56 · May 22, 2019
Author only
Bach 回复

windows的?

Bach · #58 · May 22, 2019
Author only
Bach 回复

看了下貌似是requests请求报错了

楼主,你好,请教个问题,为什么每次执行都会先退到桌面,相关逻辑代码可以帮忙指出吗?
还有就是每次自动化测试执行完后都会弹出下面的图片所示的界面,是否可以取消,不让其显示?

LinXunFeng 回复

1、退回桌面应该是setup terndown里写脚本 cls.d.app_stop("com.github.android_app_bootstrap")
2、Public下的Drivers.py 94行的 base_page.identify()注释掉就好了

linpengcheng 回复

感谢楼主😀

  • 第二点已经解决了
  • 第一点还是跟之前一样,执行的时候还是会退到桌面,如果是在桌面则回到桌面首页,类似执行了 "home" 操作

报告里多出一条这样的记录 "W/ActivityManager( 2931): Invalid packageName: com.github.android_app_bootstrap"
请问 返回桌面(首页) 这个问题还有其它的解决办法吗?

LinXunFeng 回复

什么时候回到桌面?

linpengcheng 回复

每次执行 python run_cases.py 都会回到桌面,下面是具体代码,不知道是不是我写的有问题

  • 具体的自动化操作相关代码

    class apk_install(unittest.TestCase, BasePage):
    @classmethod
    @setupclass
    def setUpClass(cls):
    print("setupClass")
    # cls.d.app_stop_all()
    cls.d.app_stop("com.github.android_app_bootstrap")
    pass

    @classmethod
    @teardownclass
    def tearDownClass(cls):
    print("tearDownClass")
    cls.d.app_stop("com.github.android_app_bootstrap")
    pass

    @testcase
    def test_install_apk(self):
    self.d.app_uninstall(pkg_name)
    # self.d.app_info(apk_url)
    self.local_install(apk_path)
    self.d.app_start(pkg_name)
  • 执行操作的相关代码

cases.addTest(test_install_apk.apk_install('test_install_apk'))
Drivers().run(cases)
LinXunFeng 回复

setup 和teardown 都杀掉app 当然会回到桌面了

linpengcheng 回复

那我弄错你之前回复的意思了,不好意思。
我遇到的问题是:本来代码里就没有加这个 cls.d.app_stop 代码的,但是在执行 python run_cases.py 也是会退到桌面,不是杀死指定程序,而是执行了home的操作(推测),有什么办法可以让它不做出这样的操作呢?

我所说的执行了home 操作的现象:

  • 如果当前设备显示app,则退回桌面(不一定是首页)
  • 如果当前设备显示桌面,则滚回到桌面首页
LinXunFeng 回复

执行cases前会check_alive 里面有一个d.healthcheck()的方法。

def healthcheck(self):
"""
Reset device into health state

Raises:
RuntimeError
"""

sh = self.ash
if not sh.is_screen_on():
print(time.strftime("[%Y-%m-%d %H:%M:%S]"), "wakeup screen")
sh.keyevent("WAKEUP")
sh.keyevent("HOME")
sh.swipe(0.1, 0.9, 0.9, 0.1) # swipe to unlock

sh.keyevent("HOME")
sh.keyevent("BACK")
self.reset_uiautomator()
LinXunFeng 回复

你可以注释掉drivers.py下check_alive方法下的d.healthcheck()的方法,但是 你运行的时候 要保证手机是不再锁屏状态的

linpengcheng 回复

好的楼主,十分感谢👍 👍 👍

请问这个是多设备并行run还是串行run的测试用例?

xiaoxiao 回复

多进程的并行

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up