pytest学习和使用20-pytest如何进行分布式测试?(pytest-xdist)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pytest学习和使用20-pytest如何进行分布式测试?(pytest-xdist)相关的知识,希望对你有一定的参考价值。

(20-pytest如何进行分布式测试?(pytest-xdist))

1 什么是分布式测试?

  • 在进行本文之前,先了解些基础知识,什么是分布式测试?
  • 分布式测试:是指通过局域网和Internet,把分布于不同地点、独立完成特定功能的测试计算机连接起来,以达到测试资源共享、分散操作、集中管理、协同工作、负载均衡、测试过程监控等目的的计算机网络测试。
  • 通俗的讲:分布式测试 就是活太多,一个人干费时间,那就让多个人一起干,节省了资源和时间。

2 为什么要进行分布式测试?

2.1 场景1:自动化测试场景

  • 自动化测试时,我们有很多用例,比如2000条用例,按照顺序执行,每条用例执行1分钟,那需要2000分钟;
  • 什么概念?2000分钟就30多个小时,如果是冒烟测试,估计还没人工跑的快;
  • 还有,如果是线上发布,跑完2000条用例就太浪费时间了;
  • 那如果我们让我们让用例分布式执行,是不是可以节省很多时间?

2.2 场景2:性能测试场景

  • 如果数据量很大,我们使用1台压测机,可能并发压力过大;
  • 那就需要选择使用多台压测机(比如Jmeter的 Agent/负载机);
  • 这样也是一种分布式压测或者分布式性能测试场景。

3 分布式测试有什么特点?

特点 说明
网格化 多节点互联互通,可资源共享
分布性 地域和计算机上,协同工作、负载均衡、可扩展性、高可用性
开放性 可移植性、可互操作性、可伸缩性、易获得性
实时性 各种信息都必须是实时的
动态性 测试过程对象和活动动态映射
处理不确定性 具有处理不确定性的能力
容错及安全性 容错能力强,可靠性高、安全性好

4 分布式测试关键技术是什么?

技术点 要求
分布式环境 获取全局状态,能够方便地监视和操纵测试过程;集中式的分布式策略。
分布式环境下的节点通信 稳定的通信环境;适合用基于消息通信的方式来实现。
测试任务调度 静态调度、动态调度和混合调度。

5 分布式执行用例的前置条件是什么?

  • 用例之间是独立且没有依赖关系,完全独立运行;
  • 用例执行没有顺序,随机顺序都能正常执行;
  • 每个用例都能重复运行,运行结果不会影响其他用例。

6 pytest-xdist安装

  • pytest-xdist让自动化测试用例分布式执行,节省测试时间,属于进程级别的并发;
  • 使用以下方法安装:
pip3 install pytest-xdist
C:\\Users\\Administrator>pip3 install pytest-xdist
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Requirement already satisfied: pytest-xdist in d:\\python37\\lib\\site-packages (1.31.0)
Requirement already satisfied: six in d:\\python37\\lib\\site-packages (from pytest-xdist) (1.15.0)
Requirement already satisfied: execnet>=1.1 in d:\\python37\\lib\\site-packages (from pytest-xdist) (1.8.0)
Requirement already satisfied: pytest>=4.4.0 in d:\\python37\\lib\\site-packages (from pytest-xdist) (6.2.4)
Requirement already satisfied: pytest-forked in d:\\python37\\lib\\site-packages (from pytest-xdist) (1.1.3)
Requirement already satisfied: apipkg>=1.4 in d:\\python37\\lib\\site-packages (from execnet>=1.1->pytest-xdist) (1.5)
Requirement already satisfied: toml in d:\\python37\\lib\\site-packages (from pytest>=4.4.0->pytest-xdist) (0.10.2)
Requirement already satisfied: attrs>=19.2.0 in d:\\python37\\lib\\site-packages (from pytest>=4.4.0->pytest-xdist) (20.3.0)
Requirement already satisfied: colorama in d:\\python37\\lib\\site-packages (from pytest>=4.4.0->pytest-xdist) (0.4.4)
Requirement already satisfied: atomicwrites>=1.0 in d:\\python37\\lib\\site-packages (from pytest>=4.4.0->pytest-xdist) (1.4.0)
Requirement already satisfied: pluggy<1.0.0a1,>=0.12 in d:\\python37\\lib\\site-packages (from pytest>=4.4.0->pytest-xdist) (0.13.1)
Requirement already satisfied: py>=1.8.2 in d:\\python37\\lib\\site-packages (from pytest>=4.4.0->pytest-xdist) (1.10.0)
Requirement already satisfied: importlib-metadata>=0.12 in d:\\python37\\lib\\site-packages (from pytest>=4.4.0->pytest-xdist) (2.1.1)
Requirement already satisfied: packaging in d:\\python37\\lib\\site-packages (from pytest>=4.4.0->pytest-xdist) (20.8)
Requirement already satisfied: iniconfig in d:\\python37\\lib\\site-packages (from pytest>=4.4.0->pytest-xdist) (1.1.1)
Requirement already satisfied: zipp>=0.5 in d:\\python37\\lib\\site-packages (from importlib-metadata>=0.12->pytest>=4.4.0->pytest-xdist) (1.2.0)
Requirement already satisfied: pyparsing>=2.0.2 in d:\\python37\\lib\\site-packages (from packaging->pytest>=4.4.0->pytest-xdist) (2.4.7)

7 pytest-xdist的优势

  • 测试运行并行化;
  • 在子进程中重复运行测试;
  • 可指定不同的Python解释程序或不同的平台,并行运行测试。

8 pytest-xdist的使用

8.1 普通执行

# -*- coding:utf-8 -*-
# 作者:虫无涯
# 日期:2023/3/16 
# 文件名称:test_xdist.py
# 作用:pytest-xdist分布式测试

import pytest
import time

class TestCase01():
    def test_case_01(self):
        time.sleep(1)
        print("case01$$$$$$$$$$$$$$$$$$$$$")

    def test_case_02(self):
        time.sleep(1)
        print("case02$$$$$$$$$$$$$$$$$$$$$")

    def test_case_03(self):
        time.sleep(1)
        print("case03$$$$$$$$$$$$$$$$$$$$$")

    def test_case_04(self):
        time.sleep(1)
        print("case04$$$$$$$$$$$$$$$$$$$$$")

    def test_case_05(self):
        time.sleep(1)
        print("case05$$$$$$$$$$$$$$$$$$$$$")

    def test_case_06(self):
        time.sleep(1)
        print("case06$$$$$$$$$$$$$$$$$$$$$")

class TestCase02():
    def test_case_07(self):
        time.sleep(1)
        print("case07$$$$$$$$$$$$$$$$$$$$$")

    def test_case_08(self):
        time.sleep(1)
        print("case08$$$$$$$$$$$$$$$$$$$$$")

    def test_case_09(self):
        time.sleep(1)
        print("case08$$$$$$$$$$$$$$$$$$$$$")


if __name__ == __main__:
    pytest.main(["-s", "test_xdist.py"])

  • 执行结果如下,使用了9.14s:
test_xdist.py::TestCase01::test_case_01 
test_xdist.py::TestCase01::test_case_02 
test_xdist.py::TestCase01::test_case_03 
test_xdist.py::TestCase01::test_case_04 
test_xdist.py::TestCase01::test_case_05 
test_xdist.py::TestCase01::test_case_06 
test_xdist.py::TestCase02::test_case_07 PASSED                           [ 11%]case01$$$$$$$$$$$$$$$$$$$$$
PASSED                           [ 22%]case02$$$$$$$$$$$$$$$$$$$$$
PASSED                           [ 33%]case03$$$$$$$$$$$$$$$$$$$$$
PASSED                           [ 44%]case04$$$$$$$$$$$$$$$$$$$$$
PASSED                           [ 55%]case05$$$$$$$$$$$$$$$$$$$$$
PASSED                           [ 66%]case06$$$$$$$$$$$$$$$$$$$$$
PASSED                           [ 77%]case07$$$$$$$$$$$$$$$$$$$$$

test_xdist.py::TestCase02::test_case_08 PASSED                           [ 88%]case08$$$$$$$$$$$$$$$$$$$$$

test_xdist.py::TestCase02::test_case_09 PASSED                           [100%]case08$$$$$$$$$$$$$$$$$$$$$


============================== 9 passed in 9.14s ==============================

8.2 上述代码分布式执行:

  • 执行命令:
pytest -s -n auto test_xdist.py
  • 结果如下,用时4.51s,可见分布式执行后大大缩短了测试时间:
(venv) F:\\pytest_study\\test_case\\test_j>pytest -s -n auto test_xdist.py
============================================ test session starts =============================================
platform win32 -- Python 3.7.0, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: F:\\pytest_study, configfile: pytest.ini
plugins: allure-pytest-2.8.12, assume-2.4.3, cov-2.8.1, forked-1.1.3, html-2.0.1, metadata-1.8.0, ordering-0.6,
 repeat-0.9.1, rerunfailures-10.3, xdist-1.31.0
gw0 [9] / gw1 [9] / gw2 [9] / gw3 [9] / gw4 [9] / gw5 [9] / gw6 [9] / gw7 [9]
.........
============================================= 9 passed in 4.51s ==============================================

8.3 指定CPU运行数量

  • -n auto:可以自动检测到系统的CPU核数;
  • 使用auto利用了所有CPU来跑用例;
  • 也可以指定使用几个CPU来跑用例:
# x为cpu个数
pytest -s -n x
  • 如下可以看到使用两个CPU来跑用例时长为6.27s:
(venv) F:\\pytest_study\\test_case\\test_j>pytest -s -n 2 test_xdist.py
============================================ test session starts =============================================
platform win32 -- Python 3.7.0, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: F:\\pytest_study, configfile: pytest.ini
plugins: allure-pytest-2.8.12, assume-2.4.3, cov-2.8.1, forked-1.1.3, html-2.0.1, metadata-1.8.0, ordering-0.6,
 repeat-0.9.1, rerunfailures-10.3, xdist-1.31.0
gw0 [9] / gw1 [9]
.........
============================================= 9 passed in 6.27s ==============================================

8.4 与pytest-html一起使用

  • 命令如下:
pytest -s -n auto --html=report.html --self-contained-html
  • 运行结果:
pytest -s -n auto test_xdist.py --html=report.thml --self-contained-htm
l
gw0 [9] / gw1 [9] / gw2 [9] / gw3 [9] / gw4 [9] / gw5 [9] / gw6 [9] / gw7 [9]
.........
------------------ generated html file: file://F:\\pytest_study\\test_case\\test_j\\report.thml ------------------
============================================= 9 passed in 4.68s ==============================================

8.5 让pytest-xdist按照指定顺序执行

  • pytest-xdist执行默认是无须的;
  • 可通过 --dist 参数来控制顺序;
参数 说明
--dist=loadscope 同一个模块module下的函数和同一个测试类class下的方法来分组
--dist=loadfile 同一个文件名来分组

8.6 pytest-xdist如何保持session执行一次

  • pytest-xdist没有内置的支持来确保会话范围的夹具仅执行一次;
  • 可使用FileLock方法仅仅产生一次fixture数据:
import pytest
from filelock import FileLock


@pytest.fixture(scope="session")
def login():
    print("====登录===")
    with FileLock("session.lock"):
        name = "zhang"
        password= "123456"
        # web ui自动化
        # 声明一个driver,再返回

        # 接口自动化
        # 发起一个登录请求,将token返回都可以这样写

    yield name, password
    print("====退出====")

pytest学习和使用5-Pytest和Unittest中的断言如何使用?

1 说明

  • pytest中使用assert进行断言,和unittest是有区别的,后边详细列举;
  • pytest中的assert后可以为表达式,为True表示用例通过。

2 Uinttest中的断言

2.1 部分断言

  • 我们写一个class,代码中就可以看到所有的断言:
  • 常用断言:
方法检查
assertEqual(a, b)a ==b
assertNotEqual(a, b)a !=b
assertTrue(x)bool(x) is True
assertFalse(x)Bool(x) is False
assertIs(a, b)a is b
assertIsNot(a, b)a is not b
assertIsNone(x)x is None
assertIsNotNone(x)x is not None
assertIn(a, b)a in b
assertNotIn(a, b)a not in b
assertIsInstance(a, b)isinstance(a,b)
assertNotIsInstance(a, b)not isinstance(a,b)

2.2 部分举例

# -*- coding:utf-8 -*-
# 作者:NoamaNelson
# 日期:2021/11/26 
# 文件名称:test_unint.py
# 作用:unittest断言
# 联系:VX(NoamaNelson)
# 博客:https://blog.csdn.net/NoamaNelson

import unittest

class TestU(unittest.TestCase):
    def setUp(self) -> None:
        self.a = 3
        self.b = 4
        self.c = 3
        self.d = 0

    def tearDown(self) -> None:
        pass

    def test_o(self):
        self.assertTrue(self.c, msg="结果为False")

    def test_t(self):
        self.assertEqual(self.a, self.c, msg="a和c不相等")


if __name__ == "__main__":
    unittest.main()

3 Pytest断言

3.1 常用断言

断言说明
assert a判断 a为真
assert not a判断 a不为真
assert a in b判断 b 包含 a
assert a == b判断 a 等于 b
assert a != b判断 a 不等于 b

3.2 表示方法

# -*- coding:utf-8 -*-
# 作者:NoamaNelson
# 日期:2021/11/26 
# 文件名称:test_assert.py
# 作用:assert断言
# 联系:VX(NoamaNelson)
# 博客:https://blog.csdn.net/NoamaNelson

import pytest


class TestU():

    def test_f(self):
        a = 3
        b = 4
        s = a + b
        return s

    def test_func(self):
        assert self.test_f() == 7, "计算结果不是7"


if __name__ == "__main__":
    pytest.main()
  • 输出为:
(venv) F:\\pytest_study\\test_case\\test_d>pytest test_assert.py
============================================ test session starts =============================================
platform win32 -- Python 3.7.0, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: F:\\pytest_study\\test_case\\test_d
plugins: allure-pytest-2.8.12, cov-2.8.1, forked-1.1.3, html-2.0.1, metadata-1.8.0, ordering-0.6, xdist-1.31.0
collected 2 items

test_assert.py ..                                                                                       [100%]

============================================= 2 passed in 0.31s ==============================================

3.3 异常断言

  • 可以使用 pytest.raises 作为上下文管理器,当抛出异常时可以获取到对应的异常实例;
  • 目的是断言抛出的异常是不是预期想要的;
  • 比如如下,断言1 / 0的异常是不是ZeroDivisionError,其中ZeroDivisionError是异常类型,用type从异常信息中获取;division by zero是异常的值,使用value从异常信息中获取。
    def test_a(self):
        with pytest.raises(ZeroDivisionError) as e:
            1 / 0

        assert e.type == ZeroDivisionError
        assert "division by zero" in str(e.value)

『全栈测试技术,分享,共勉,共进,提升』


以上是关于pytest学习和使用20-pytest如何进行分布式测试?(pytest-xdist)的主要内容,如果未能解决你的问题,请参考以下文章

pytest学习和使用10-Pytest中的测试用例如何跳过执行?

pytest学习和使用5-Pytest和Unittest中的断言如何使用?

pytest学习和使用16-HTML报告如何生成?(pytest-html)

pytest学习和使用13-Pytest的fixture如何使用request传入参数?

pytest学习和使用13-Pytest的fixture如何使用request传入参数?

pytest学习和使用18-pytest.ini配置文件如何使用?