目前团队在使用的工具是pytest+u2。并通过python的celery异步与jenkins做结合达到打包完成后自动执行测试的目的。最后需要回传测试结果到外网的数据库中并在钉钉同步测试结果。
UI自动化,不同于接口,在case量级上来之后执行时间往往以小时计。
目前App自动化项目情况:
case量:1000条左右
执行时间:6h+
case低耦合
思路
这次写的自动化项目初衷是可以兼容android、ios、web、api多种测试,所以不考虑使用自动化工具本身自带的多进程或多线程功能,最好在测试管理层做协调同步,方便统一维护调用。
pytest有两个插件可以实现 pytest-xdist(仅支持多进程)、pytest-parallel(Windows多进程不可用)
最后选用pytest-xdist(本机是Windows)
通过fixture 实现测试过程测试设备的作用域,避免测试对象变化。
通过文件锁,在文件中写入空闲测试对象的内存地址,实现n个进程共用n个设备时的冲突。
当执行完一个测试后,进程释放测试设备,并记录设备的内存地址到文件中,下一个测试开始前通过_ctypes.PyObj_FromPtr方法将内存地址转化为测试对象。
最后,通过进程锁,完成测试结果的收集与回传。
失误
想好之后,撸上袖子就开始干,在写核心方法的时,无意间发现xdist官方文档中的这段:
他特么实现了。通过进程名+文件锁实现了多进程间的数据共享。
后边就在xdist提供的两个参数基础上完成了实现。
最终实现
对比最早的想法,最终的实现方式省略了内存地址和对象间的转换、多设备并发时可能存在的设备冲突。改为在每个进程初始化其所用设备时做设备的分配,省去了大部分代码量。
# 获取设备列表
def devices():
a = os.popen('adb devices').readlines()
if len(a) >= 2:
dl = [i.split('\t')[0] for i in a if '\t' in i]
else:
dl = 'adb 异常,未获取到设备'
return dl
#初始化设备
@pytest.fixture(scope="session", autouse=True)
def driver(tmp_path_factory, worker_id):
devices_list = drivers.android.devices()
if not devices_list:
raise
global device
if worker_id == "master": # 单进程
device = drivers.android.device_android(devices_list[0])
return device
root_tmp_dir = tmp_path_factory.getbasetemp().parent
fn = root_tmp_dir / "devices.json"
with FileLock(str(fn) + ".lock"):
log.info(fn.is_file())
if fn.is_file():
devices_ls = fn.read_text()
if not devices_ls:
return Exception('No Device')
device_serial, devices_l = _devices_list_pop(devices_ls)
device = drivers.android.device_android(device_serial)
fn.write_text(
str(devices_l)
)
else:
device = drivers.android.device_android(devices_list[0])
devices_list.pop(0)
fn.write_text(str(devices_list))
return device
# 处理文件中读取的设备号
def _devices_list_pop(dl):
d = dl[1:-1].replace("'", '').strip().split(', ')
ds = d[0]
d.pop(0)
return ds, d
收获
官方文档多数情况下挺香的