使用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官網》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM