zoukankan      html  css  js  c++  java
  • 使用pytest-xdist实现分布式WEB自动化测试

    前言

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

    进程并发

    使用pytest命令

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

    获取安装包

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

    项目介绍

    项目结构

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

    项目介绍

    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
    

    Windows + Linux主从分布式

    系统环境

    一开始,我选择了本机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

    同步测试用例并运行

    既然要做主从分布式,那么就需要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,这样行不行呢?

    三台Linux主从分布式

    系统环境

    这里的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

    分布式运行

    重新运行之前,注意将项目上传到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

    单进程运行

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

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

    多进程运行

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

    4个进程

    计算次方耗时对比

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

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

    WEB自动化分布式

    安装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
    

    安装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,三台机器都需要安装

    修改模块名

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

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

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

    分布式运行

    在试验之前,建议把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

    单进程运行

    运行用例100条,耗时4min57s

    多进程运行

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

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

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

    WEB运行耗时对比

    环境 核数 用例数量 耗时 备注
    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

    配置文件

    和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
    

    each模式和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模式

    APP自动化分布式设想

    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自动化设计的原则

    参考文章

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

  • 相关阅读:
    一个好的时间函数
    Codeforces 785E. Anton and Permutation
    Codeforces 785 D. Anton and School
    Codeforces 510 E. Fox And Dinner
    Codeforces 242 E. XOR on Segment
    Codeforces 629 E. Famil Door and Roads
    Codeforces 600E. Lomsat gelral(Dsu on tree学习)
    Codeforces 438D The Child and Sequence
    Codeforces 729E Subordinates
    【ATcoder】D
  • 原文地址:https://www.cnblogs.com/my_captain/p/12775010.html
Copyright © 2011-2022 走看看