使用pytest-xdist实现分布式WEB自动化测试

1|0前言


pytest-xdist是一款优秀的分布式测试插件,它可以实现进程级别的并发,也可以实现类似于master-worker主从分布式测试。目前中文网站对于进程级别的并发介绍的比较多,对于主从分布式测试的资料少之又少。经过反复的实践,对于主从分布式环境的部署和运行有了一定的认知,因此,在本文中将着重介绍主从分布式测试,对于进程并发只做简单的介绍

2|0进程并发


使用pytest命令

pytest -s -n 2 #-n 后面跟核数,如果你的CPU是4核的,那么2表示使用2个核来并发2个进程执行测试 pytest -s -n auto #auto会自动检测你的CPU核数,如果是4核,将并发4个进程

3|0获取安装包


这里面有一些Linux下的安装包,还有我自己用来练手的demo项目WEB_AutoTest
链接:https://pan.baidu.com/s/14vnqtQufXr2Jj1HuACJa9A
提取码:bgdp
其他资料没有准备,因此在开始试验前需要自行安装 pytest,pytest-html,pytest-xdist,selenium

4|0项目介绍


4|1项目结构


WEB_AutoTest |--test_cases |--__init__.py |--test_caculate.py |--search.py |--pytest.ini

4|2项目介绍


test_caculate.py是让python自己不停的做次方运算,这是试水项目,不建议一上来就执行web自动化,先搞个demo试试,能运行起来再搭建web自动化环境

# test_caculate.py import pytest @pytest.mark.parametrize("n", list(range(50000))) def test_baidu(n): a = 2 ** 32 print(f"the baidui-{n}.") @pytest.mark.parametrize("n", list(range(50000))) def test_sina(n): b = 4 ** 32 print(f"the sina-{n}")

search.py的作用是使用无头浏览器不停的打开百度,然后输入关键字,查找网页响应的元素,最后做断言。当test_caculate.py通过之后,可以将其名字更改为test_search.py,将前者改成caculate.py,这样就只会运行web自动化测试

# search.py from selenium import webdriver import pytest @pytest.mark.parametrize("n", list(range(100))) def test_baidu(n): # 创建chrome参数对象 options = webdriver.ChromeOptions() options.add_argument('--no-sandbox') # 解决DevToolsActivePort文件不存在的报错 # options.add_argument('window-size=1600x900') # 指定浏览器分辨率 options.add_argument('--disable-gpu') # 谷歌文档提到需要加上这个属性来规避bug options.add_argument('--hide-scrollbars') # 隐藏滚动条, 应对一些特殊页面 options.add_argument('blink-settings=imagesEnabled=false') # 不加载图片, 提升速度 options.add_argument('--headless') # 浏览器不提供可视化页面. linux下如果系统不支持可视化不加这条会启动失败 driver = webdriver.Chrome(options=options) driver.get('http://www.baidu.com') title = driver.title url = driver.current_url #输入百度 driver.find_element_by_id("kw").send_keys("百度") elements = driver.find_elements_by_xpath("//h3//em") for element in elements: assert element.text == "百度" #点击百度一下 driver.find_element_by_id("su").click() assert title == "百度一下,你就知道" assert url == "https://www.baidu.com/" assert type(n) == int driver.quit()

pytest.ini是一个配置文件,稍后会说明其作用

# pytest.ini [pytest] #addopts = --tx socket=192.168.0.109:8888

5|0Windows + Linux主从分布式


5|1系统环境


一开始,我选择了本机Windows做master,虚拟机Centos7做worker1,同时还克隆了一台Centos7作为worker2。有关的环境版本如下:

角色 系统 Python版本 ip
master Windows7 v3.7.3 192.168.0.101
worker1 Centos7.6 v3.8.0 192.168.0.109
worker2 Centos7.6 v3.8.0 192.168.0.126

5|2同步测试用例并运行


既然要做主从分布式,那么就需要master将测试用例同步给worker。在官网上有两种同步方式:通过远程的SSH账号和通过远程的socket服务。前者我琢磨了比较久,发现怎么试都不成功,于是就直接试后者,后者在官网上的介绍基本能让人看懂:

基本上分为两步:
1.将socketserver.py文件上传到你的服务器,然后这样运行:

python socketserver.py

socketserver.py的文件在我分享的安装包里有,只需下载下来,通过rz命令上传到你的服务器上。我放在了/opt目录下,随便放在哪个目录都行,只要你记得路径就行。接着运行socketserver.py,socket服务就启动了,开始监听8888端口

2.然后在本机(Windows)上运行同步命令

pytest -d --tx socket=192.168.0.109:8888 --tx socket=192.168.0.126:8888 --rsyncdir test_cases test_cases

在工程目录下打开命令行运行。一开始还是好的,没多久Centos7上的socket服务就挂了


截两张socket服务挂了的图
worker1

worker2

有个细节值得注意pytest -d --tx socket=xxxx --rsyncdir xxxx xxxx,这个命令在同步完之后,会自动收集用例并执行。虽然执行的过程挂了,但用例确实同步成功了。就在下图的pyexecnetcache目录下

关于这个服务挂了的问题,github上已经提了一些issue:
With different python versions rsync can hangs on
pytest hangs indefinitely after completing tests in parallel
通过对第一个问题的查看,发现是由于Python版本不一致导致的。接着我更新了两台Centos7上的版本,将它们都改为Python v3.7.7,发现还是会报同样的错误。然后我设想,可以再克隆一台虚拟机,三台Centos7,一个master,两个slave,这样行不行呢?

6|0三台Linux主从分布式


6|1系统环境


这里的Python版本无论是v3.7.7还是v3.8.0都行,只要一致就可以

角色 系统 Python版本 ip
master Centos7.6 v3.8.0 192.168.0.109
worker1 Centos7.6 v3.8.0 192.168.0.126
worker2 Centos7.6 v3.8.0 192.168.0.136

6|2分布式运行


重新运行之前,注意将项目上传到master上

在worker1和worker2分别运行socket.server
接着进入项目目录WEB_AutoTest,开始执行同步命令

pytest -d --tx socket=192.168.0.126:8888 --tx socket=192.168.0.136:8888 --rsyncdir test_cases test_cases

这次正常了,我们可以看到这些运行截图
master

worker1

worker2

运行结束之后,发现总共运行用例100000条,耗时8min52s。其中,worker1运行了47540条,约占47%,耗时8min41s,worker2运行了52460条,约占52%,耗时8min40s
master

worker1

worker2

6|3单进程运行


我现在使用master单独运行这100000条用例,看看效果

可以看到运行100000条用例,master单进程跑只花了4min24s

6|4多进程运行


如果我给master分配4核,跑2个进程和跑4个进程呢?
2个进程

4个进程

6|5计算次方耗时对比


可以看出,计算型的用例,好像是CPU核数也多,时间越慢。另外分布式的作用好像也不大

环境 核数 用例数量 耗时 备注
Centos7主从分布式 1 100000 8min52s 1 Centos7 master + 2 Centos7 worker
Centos7单进程 1 100000 4min22s
Centos7双进程并发 2 100000 8min38s
Centos7四进程并发 4 100000 11min09s

7|0WEB自动化分布式


7|1安装goole-chrome-stable


将安装包内的google-chrome-stable_current_x86_64.rpm上传到/opt目录下,使用yum安装

yum install ./google-chrome-stable_current_x86_64.rpm

安装成功后,可以使用下面的命令来查看chrome版本

[root@localhost opt]# google-chrome --version Google Chrome 81.0.4044.122

7|2安装chromedriver


将安装包内的chromedriver_linux64.zip上传到服务器,解压后将它放在/opt/Python-3.8.0/bin目录下,这个是python可执行文件所在的目录,已经配置环境变量了
同样可以使用命令查看chromedriver对应的版本,这个版本和chrome版本一定要对应好

[root@localhost opt]# chromedriver --version ChromeDriver 81.0.4044.69 (6813546031a4bc83f717a2ef7cd4ac6ec1199132-refs/branch-heads/4044@{#776})

注意:以上说的chrome和chromedriver,三台机器都需要安装

7|3修改模块名


要运行WEB自动化了,不希望执行次方运算,可以使用mv命令重命名下模块名

在一切就绪前,先在本地跑个简单的程序试试水,建议把test_search.py中的参数化改成1次,直接在master运行,看配置的环境有没有问题

正常情况下,运行完应该断言成功

7|4分布式运行


在试验之前,建议把test_search.py的参数化运行次数改成原来的100

方法和前面一样,启动worker上的socket服务后,使用命令

pytest -d --tx socket=192.168.0.126:8888 --tx socket=192.168.0.136:8888 --rsyncdir test_cases test_cases

可以看到,总共运行100条用例,总耗时2min42s,其中worker1运行用例52条,耗时2min42s,worker2运行用例28条,耗时2min40s
master

worker1

worker2

7|5单进程运行


运行用例100条,耗时4min57s

7|6多进程运行


2个进程
100条用例耗时2min50s

4个进程
4个进程甚至更快,总耗时才1min40s

不过还要考虑一种情况,就是分布式有个同步用例的过程,现在我把同步用例的过程去掉,试试分布式的时间。看样子也要2min32s

7|7WEB运行耗时对比


环境 核数 用例数量 耗时 备注
Centos7主从分布式 1 100 2min42s 1 Centos7 master + 2 Centos7 worker
Centos7单进程 1 100 4min57s
Centos7双进程并发 2 100 2min50s
Centos7四进程并发 4 100 1min40s
Centos7主从分布式不同步 1 100 2min32s

8|0配置文件


和pytest的pytest.ini一样,你可以在pytest.ini中做一些配置,比如想要三个进程执行就可以使用配置

[pytest] addopts = -n3

如果之前已经同步了一次测试用例,这次想要直接运行,但又不想跟特别长的--tx怎么办

[pytest] addopts = --tx socket=192.168.0.126:8888 --tx=socket=192.168.0.136:8888

这样配置之后,你可以根据情况来选择运行

pytest --dist=each

或者

pytest --dist=load

9|0each模式和load模式


这两个模式的解释最后才找到,官网藏得比较隐蔽。你可以点击这里查看-->dist的模式
--dist=each:master将所有的测试用例都分发到各个worker上,相当于worker1执行所有的100条用例,worker2执行所有的100条用例。运行完master上显示的总用例个数是200条
--dist=load:master先将25%的用例以循环的方式分发给每个worker,剩下的用例等worker执行完之后再分发。有点nginx负载均衡的感觉,worker负载过高,master会将其负载降低一些,让其他worker分摊一点。这种模式运行的总用例个数是100条,worker1和worker2运行的用例数不定,有可能各是50条,有可能一个是48条,一个是52条。就像我们之前看到的那样
使用pytest -d --tx socket=xxxx rsyncdir xxxx xxxx这种同步方式运行,默认的是load模式

10|0APP自动化分布式设想


WEB自动化之所以简单,是因为只要chromedriver和chrome版本一致,问题应该不是很大。但APP就不同了,APP涉及到的东西较多,首先必须要有真机或者模拟器,然后还要启动appium服务端,在脚本中将desired_caps(比如platformName,packageName,activityName,systemVersion等)传递给appium服务端,再由服务端返回driver来操作客户端
因此APP自动化也要保证环境的一致性,即所装的APP版本,模拟器系统版本、appium的端口号都要一致,因为都是一套代码推送到不同的worker。可以像之前那样继续使用docker,注意的是两个worker的appium容器的docker_name必须一样,这样才可以在脚本里使用docker docker_name的方式启动appium服务
还有一个问题是,如果master和worker都是Centos7,就要考虑模拟器怎么和远程的worker连起来,之前尝试过通过tcpip来连,是可以成功的。这个tcpip端口号可以设置不同,因为desired_caps里对这个deviceName没要求。这么来看,APP自动化也是具有可行性的
事实上,分布式测试对用例的独立性要求很高,尽量避免不必要的依赖,这也是UI自动化设计的原则

11|0参考文章


《pytest-xdist官网》
《Pytest系列(16)- 分布式测试插件之pytest-xdist的详细》
《Pytest系列(17)- pytest-xdist分布式测试的原理和流程》
《CentOS7下无界面使用Selenium+chromedriver进行自动化测试》
《execnet官网》


__EOF__

本文作者cnhkzyy
本文链接https://www.cnblogs.com/my_captain/p/12775010.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   cnhkzyy  阅读(2211)  评论(2编辑  收藏  举报
编辑推荐:
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
点击右上角即可分享
微信分享提示