抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

背景

随着自动化case数量的增长,执行一次耗时也越来越长,目前生产环境大概需要40分钟才能跑完所有case!时间,时间就是生命,需要想办法来提高运行速度。

img

一般情况下我们会想先优化代码,看看哪边耗时比较长,常规的优化可以分为三个步骤:数据准备, 执行操作,结果检查。

这边数据大多在testcollection直接传递,而结果检查现在大部分的断言并不复杂,因此数据准备,结果检查就不多提了。执行操作,主要是超时时间设置,还有些如减少time.sleep的操作等等。

今天直接上大招:多进程。

多进程运行

pytest本身不支持多进程,需要配合其他组件来完成,常用的pytest-xdist ,pytest-parallel。两者的区别在于pytest-xdist是多进程运行,有几个CPU就可以设置到多大,而pytest-parallel支持多进程和多线程,可以设置N个进程,每个进程M个线程,最后是N * M个线程同时运行。

(以下为未解决登录互斥问题前的数据)

未使用多进程之前

case数 时间 进程数 失败case数
143 654s 1 0

pytest-xdist

case数 时间 进程数 失败case数
143 254s 2 0
143 215.23s 2 0

pytest-parallel

case数 时间 进程数 每个进程最大线程数 失败case数
143 68s 2 4 9
143 83.02s 2 4 8
143 65.75s 2 4 7
143 119 2 2 5
143 229.7 2 1 0

以上都是直接使用的情况,本身应用就会一直重复登录,但原本是单进程,现在多进程跑,难免会有被登出的情况,case执行的失败率也会提升。

img

解决重复登录问题

​ 以上为考虑登录互斥的问题,我们先看下普通模式下的登录情况,在workflow/erpapi/pclogin.pygetloginsession 下加log。

img

常规模式下

简单点,只跑一个文件

1
pytest testcollection/freelanderapi/test_checkbillorder_sup.py

img

7接口,近14次登录(包括saas的登录和供应商端的登录),也就是说每个接口都去登录了。

解决单进程登录问题

在conftest下添加代码

1
2
3
4
@pytest.fixture(scope="session")
def once_login(tmp_path_factory, worker_id):
login_instance = LoginApi(needloginPc=True)
yield login_instance

简单改造下test_checkbillorder_sup.py文件

img
这里CheckBillOrderFlow是一串动作。

运行test_checkbillorder_sup.pytest_ordermanage.py 两个文件

1
pytest testcollection/freelanderapi/test_checkbillorder_sup.py testcollection/freelanderapi/test_ordermanage.py

img

只登录了一次!

现在单进程下,登录问题就解决了。

改造以支持多进程登录一次

改造前

使用两个进程

1
pytest -n 2 testcollection/freelanderapi/test_checkbillorder_sup.py testcollection/freelanderapi/test_ordermanage.py

img

使用三个进程

img

我们发现设置*@pytest.fixture(scope="session")*并不能解决多进程下的登录互斥问题,每个进程都会去执行一次fixture。

经过一番搜索,找到了解决方法,使用filelock模块的FileLock功能。

改造下刚刚的once_login方法

1
2
3
4
5
@pytest.fixture(scope="session")
def once_login(tmp_path_factory, worker_id):
with FileLock("session.lock"):
login_instance = LoginApi(needloginPc=True)
yield login_instance

其原理是,只执行一次scope=”session”的fixture方法,其他进程从lock文件中读取。

但是显然,这么做是无效的,我们现在统一用LoginApi去调用每个模块,它是一个对象,对象如何能写入文件呢 因此这边需要写入的是token一类的值,用于接口登录。

改造思路

现有流程大致是 testcollection –> workflow –> business –> baseinterface

现在是每初始化一次workflow都去初始化了一次f6api,到business发请求这步,是带着登录后的session状态去发的请求,而现在不传递session,就需要从头到尾进行改造。

  1. 首先,fixture方法处同时登录三个端,返回cookies

  2. 在LoginApi类接收cookies,初始化freelander, maintain, stock等的时候传对应业务的cookie

  3. 在如Freelanderapi处接收cookies,初始化该业务下接口的时候(business),传cookie

  4. 改造每一个business下的方法,接收cookie, 并且sendReq中调用发送请求的时候带上cookie

  5. 改造baseinterface方法接收cookie发送请求

工程量较大,这边单独拉一个分支,并且只保留了freelander的内容进行测试。

  • conftest.py下的once_login方法
1
2
3
4
5
6
7
8
9
10
11
@pytest.fixture(scope="session")
def once_login(tmp_path_factory, worker_id):
root_tmp_dir = tmp_path_factory.getbasetemp().parent
fn = root_tmp_dir / "data.json"
with FileLock(str(fn) + ".lock"):
if fn.is_file():
cookies = json.loads(fn.read_text())
else:
cookies = LoginPc().getToken()
fn.write_text(json.dumps(cookies))
return cookies

改造后

试验pytest-xdistimg

改造后, 可以看到供应商账号和修理厂账号都只登录了一次, 同样执行143条case,时间来到了73.94秒,第二次执行67.81秒,比单纯使用多进程快了2分钟多,看起来账号重复登录消耗了太多时间!

case数 时间 进程数 失败case数
143 73.94s 2 8
143 67.71 2 8

试验pytest-parallel

在pytest-parallel下不知道为什么,没有执行once_login后续所有case全都失败😅,暂时先不看了。

总结

从速度上来说,无疑pytest-parallel更甚一筹,但是要确保只登录一次,暂时pytest-paraller无法搭配使用,pytest-xdist就成了首选,速度是上来了,也带来了一些错误率,多是在复杂流程的接口,执行太快了,也带来了问题,需要更合理的设置等待条件,等待时间。

评论