說明
前面文章介紹了如何利用
docker
搭建selenium grid
selenium grid
由一個中心Hub
節(jié)點和大于等于1個的Node
節(jié)點組成鸳谜,所有的Node
節(jié)點都要注冊到Hub
節(jié)點悦冀。測試執(zhí)行時所有的請求發(fā)送到Hub
節(jié)點邻遏,Hub
節(jié)點再將執(zhí)行指令分發(fā)到多個注冊的Node
節(jié)點髓削,Node
節(jié)點是真正執(zhí)行Web測試的節(jié)點驯用,就相當于selenium
本機執(zhí)行一樣驼鞭。網(wǎng)上很多教程都是使用多進程/多線程啟動多個
node
去執(zhí)行用例,這樣的意義并不大衫贬,如果一個Node
中的用例太多德澈,并不會節(jié)約多少時間,如果開啟太多的進程用node
去跑用例固惯,無論是管理用例的復(fù)雜性和損耗資源都不是成正比正確的使用場景是一個
node
里面再去分布式執(zhí)行用例圃验,其實java
中的testng
提供這樣的功能,而此次我介紹的是用python
缝呕,因此需要集結(jié)合pytest
環(huán)境搭建
本機
window10
安裝好python3
pytest
pytest-html
生成測試報告插件pytest-xdist 分布式用例
pip install pytest
pip install pytest-xdist
pip install pytest-html
- 修改
pytest
源代碼文件,解決報告亂碼問題
D:\app\Python37\Lib\site-packages\pytest_html\plugin.py
class TestResult:
def __init__(self, outcome, report, logfile, config):
#self.test_id = report.nodeid.encode("utf-8").decode("unicode_escape")
self.test_id = re.sub(r'(\\u[a-zA-Z0-9]{4})',lambda x:x.group(1).encode("utf-8").decode("unicode-escape"),report.nodeid)
-
看下我的代碼結(jié)構(gòu)
image.png 核心目錄是
testcase
是用例目錄斧散,里面分為了大回歸供常、小回歸、冒煙文件夾鸡捐,用例放不同的用例栈暇,這樣的放的好處非常明顯了,大回歸包含小回歸和冒煙箍镜,小回歸包含冒煙testcase
目錄下由conftest.py
這里面對pytest
和pytest-html
可以進行有些設(shè)置
# conftest.py
import pytest
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from py._xmlgen import html
_driver = None
# @pytest.fixture()
@pytest.fixture(scope='session', autouse=True)
def driver():
global _driver
print(11111)
ip = "遠程ip"
server = "http://%s:7777/wd/hub" % ip
# ip = "localhost"
_driver = webdriver.Remote(
command_executor="http://%s:7777/wd/hub" % ip,
desired_capabilities=DesiredCapabilities.CHROME
)
# 返回數(shù)據(jù)
yield _driver
# 實現(xiàn)用例后置
_driver.quit()
@pytest.mark.hookwrapper
def pytest_runtest_makereport(item):
"""
當測試失敗的時候源祈,自動截圖煎源,展示到html報告中
:param item:
"""
if not _driver:
return
pytest_html = item.config.pluginmanager.getplugin('html')
outcome = yield
report = outcome.get_result()
report.description = str(item.function.__doc__)
extra = getattr(report, 'extra', [])
if report.when == 'call' or report.when == "setup":
xfail = hasattr(report, 'wasxfail')
if (report.skipped and xfail) or (report.failed and not xfail):
screen_img = _capture_screenshot()
if screen_img:
html = '<div><img src="data:image/png;base64,%s" alt="screenshot" style="width:1024px;height:768px;" ' \
'onclick="window.open(this.src)" align="right"/></div>' % screen_img
extra.append(pytest_html.extras.html(html))
report.extra = extra
def pytest_html_results_table_header(cells):
cells.insert(1, html.th('用例名稱'))
cells.insert(2, html.th('Test_nodeid'))
cells.pop(2)
def pytest_html_results_table_row(report, cells):
cells.insert(1, html.td(report.description))
cells.insert(2, html.td(report.nodeid))
cells.pop(2)
def pytest_html_results_table_html(report, data):
if report.passed:
del data[:]
data.append(html.div('通過的用例未捕獲日志輸出.', class_='empty log'))
def pytest_html_report_title(report):
report.title = "pytest示例項目測試報告"
def _capture_screenshot():
"""
截圖保存為base64
:return:
"""
return _driver.get_screenshot_as_base64()
- 用例編寫
# test_selenium.py
mport os
import time
import pytest
class TestCase(object):
@pytest.mark.finished
def test_001(self, driver):
time.sleep(3)
driver.get("https://www.baidu.com")
print(driver.title)
driver.find_element_by_id("kw").click()
driver.find_element_by_id("kw").send_keys("你好")
def test1_001(self, driver):
time.sleep(3)
driver.get("https://www.baidu.com")
print(driver.title)
driver.find_element_by_id("kw").click()
driver.find_element_by_id("kw").send_keys("你好")
- 代碼入口
import os
from multiprocessing import Process
import pytest
def main(path):
# pytest.main(['%s' %path,'-m finished', '--html=report.html','--self-contained-html', '--capture=sys'])
pytest.main(['%s' %path,'-n 3', '--html=report.html','--self-contained-html', '--capture=sys'])
# pytest.main(['%s' %path,'-n=auto', '--html=report.html','--html=report.html', '--html=report.html'])
# 'pytest -s 大回歸/小回歸/ --workers 1 --tests-per-worker 2 --html=report.html --self-contained-html --capture=sys'
# 'pytest -s testcase/大回歸/小回歸/冒煙 --th 10 --html=report.html --self-contained-html --capture=sys'
# 'pytest -s testcase/大回歸/小回歸/冒煙 -n 3 --html=report.html --self-contained-html --capture=sys'
if __name__ == '__main__':
# 大回歸
test_case = Process(target=main, args=("d:\\project\\auto_web_ui\\testcase\\大回歸\\",))
test_case.start()
test_case.join()
# 小回歸
# 冒煙
其他
-
pytest-xdist
經(jīng)過測試,在云服務(wù)器(雙核)上跑香缺,可以正常跑手销,如果指定進程太大,會造成容器內(nèi)存泄漏图张,服務(wù)器出現(xiàn)長期卡死锋拖,所以建議:每次執(zhí)行任務(wù)時,都把容器刪了重建祸轮,同時進程不要指定太大- 可以進入到
docker
容器中排除內(nèi)存情況:docker exec -it ec3d30bff042 top
兽埃,其中ec3d30bff042
是selenium/node-chrome
的鏡像
- 可以進入到
測試了
pytest-parallel
這個無論是在服務(wù)器還是本地win上跑,都報錯-
使用了
pytest-multithreading
發(fā)現(xiàn)個問題pytest-html
上的記錄日志适袜,會被打亂柄错,因此如果要使用的化,建議在conftest.py
中苦酱,記錄日志的代碼去掉多線程訪問百度網(wǎng)站售貌,會被限制輸入驗證信息
安裝
pip install pytest-multithreading -i https://pypi.douban.com/simple
調(diào)用
pytest -s testcase/大回歸/小回歸/冒煙 --th 10 --html=report.html --self-contained-html --capture=sys
總結(jié)
當然如果沒有條件,你對本地搭建
selenium grid
有興趣的化躏啰,可以參考我之前的這篇文章趁矾,源代碼都提供好了后續(xù)工作就更加簡單,比如jenkins啟動给僵,我會對接一個可視化平臺實現(xiàn):任務(wù)管理毫捣,報告分析等