这才是你要的分布式测试

说明

  • 前面文章介绍了如何利用docker搭建selenium grid

  • selenium grid由一个中心Hub节点和大于等于1个的Node节点组成,所有的Node节点都要注册到Hub节点。测试执行时所有的请求发送到Hub节点,Hub节点再将执行指令分发到多个注册的Node节点,Node节点是真正执行Web测试的节点,就相当于selenium本机执行一样。

  • 网上很多教程都是使用多进程/多线程启动多个node去执行用例,这样的意义并不大,如果一个Node中的用例太多,并不会节约多少时间,如果开启太多的进程用node去跑用例,无论是管理用例的复杂性和损耗资源都不是成正比

  • 正确的使用场景是一个node里面再去分布式执行用例,其实java中的testng提供这样的功能,而此次我介绍的是用python,因此需要集结合pytest

环境搭建

  • 本机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)
  • 看下我的代码结构


    image.png
  • 核心目录是testcase是用例目录,里面分为了大回归、小回归、冒烟文件夹,用例放不同的用例,这样的放的好处非常明显了,大回归包含小回归和冒烟,小回归包含冒烟

  • testcase目录下由conftest.py 这里面对pytestpytest-html可以进行有些设置

# 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
    )
    # 返回数据
    yield _driver
    # 实现用例后置
    _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 经过测试,在云服务器(双核)上跑,可以正常跑,如果指定进程太大,会造成容器内存泄漏,服务器出现长期卡死,所以建议:每次执行任务时,都把容器删了重建,同时进程不要指定太大

    • 可以进入到docker 容器中排除内存情况:docker exec -it ec3d30bff042 top,其中ec3d30bff042selenium/node-chrome 的镜像
  • 测试了pytest-parallel 这个无论是在服务器还是本地win上跑,都报错

  • 使用了pytest-multithreading 发现个问题

    • pytest-html上的记录日志,会被打乱,因此如果要使用的化,建议在conftest.py中,记录日志的代码去掉

    • 多线程访问百度网站,会被限制输入验证信息

    • 安装pip install pytest-multithreading -i https://pypi.douban.com/simple

    • 调用 pytest -s testcase/大回归/小回归/冒烟 --th 10 --html=report.html --self-contained-html --capture=sys

总结

  • 当然如果没有条件,你对本地搭建selenium grid有兴趣的化,可以参考我之前的这篇文章,源代码都提供好了

  • 后续工作就更加简单,比如jenkins启动,我会对接一个可视化平台实现:任务管理,报告分析等

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容