Python 多线程运行 pytest 用例,解决 pytest-xdist session 级别的 fixture 运行次数不合理和进程切换更消耗 cpu 的问题

random 测试交流14395字数 1143阅读3分48秒阅读模式

背景

在用 pytes 做自动化测试的时候,有时候我们的用例会非常多,特别是在用例参数化的时候。很容易就会有上千条的用例。可能每个用例又会有 IO 等待。如果顺序运行的话,就会很耗时。所以希望可以并发运行用例。这时可以用 pytest-xdist。这个网上有很多教程,这里不说了。主要说一下 pytest-xdist 的缺点

pytest-xdist 的缺点

  • pytest-xdist 是多进程运行的,进程切换很耗资源。并发不能调太大。否则会把 cpu 消耗光。如果用 pytest-xdist 同时运行几千个有 IO 等待的用例, 会立刻把 cpu 耗尽。
  • pytest-xdist 的运行机制破坏了 pytest 对 fixture 原始定义。按照原始的定义,session 级别的 fixture,在一次自动化测试中只会运行一次。但是 pytest-xdist 是每个进程都会运行一次。相当与一些全局性的前置条件会执行多次,在有些情况下可能会有问题的。比如你想在运行所有测试用例之前,先在 session 级别的 fixture 重启测试的程序,pytest-xdist 的每个进程都会执行一次,相当于会重启多次。有可能会出现某些进程已经在运行测试用例了,但另一些进程还在 session 级别的 fixture 里重启服务。影响了测试用例的运行。

为了解决上述问题。我为 pytest 写了这个插件。我把它叫做 pytest-multithreading。有以下好处文章源自玩技e族-https://www.playezu.com/240315.html

  • 1.它是基于多线程实现的。线程间的切换消耗的 cpu 更少。所以可以更高并发的运行用例。
  • 2.session 级别的 fixture 也只会运行一次。不会像 pytest-xdist 运行多次
  • 3.增加了一个哪些用例不并发运行的功能

pytest-multithreading 的基本使用方法

先安装 pytest-multithreading。直接输入 pip install pytest-multithreading -i https://pypi.douban.com/simple安装文章源自玩技e族-https://www.playezu.com/240315.html

运行时用--th 参数指定最大创建的线程数,就可以并发运行测试用例了。
以下是使用示例文章源自玩技e族-https://www.playezu.com/240315.html

import time
import pytest
test_data_list = [i for i in range(1,1000)]
@pytest.mark.parametrize("i",test_data_list)
def test_multithreading(i):
time.sleep(2)
assert 1 == 1
if __name__ ==  "__main__":
#--th是指定最大线程数,即最大并发运行的用例。这1000个用例会并发运行
pytest.main(["test_pytest.py","-x","--th=1000"])

pytest-multithreading 插件可以标记特定的测试用例不并发运行

我们知道如果想要并发运行测试用例。就要让所有的用例都没有依赖关系,每个用例不能干扰其它用例的运行。但是有些用例要 reload 程序,这会干扰其它用例的运行。所以要让这些用例独立运行,不能让其参与并发运行。所以 pytest-multithreading 做了这个支持标记那些用例不并发运行的功能文章源自玩技e族-https://www.playezu.com/240315.html

使用示例如下:文章源自玩技e族-https://www.playezu.com/240315.html

import time
import pytest
test_data_list = [i for i in range(1,100)]
@pytest.mark.parametrize("i",test_data_list)
def test_multithreading(i):
print("test_concurrent")
#被@pytest.mark.notconcurrent标记的用例不会参与并发运行,并发运行的用例会等这些被标记的用例运行结束后才开始并发运行
@pytest.mark.notconcurrent
def test_notconcurrent():
print("test_notconcurrent")
time.sleep(10)
if __name__ ==  "__main__":
pytest.main(["test_pytest.py","-x","--th=100","-s"])

pytest-multithreading 与 pytest-parallel 的对比

一开始写 pytest-multithreading 的时候,是为了可以更高并发的运行 pytest 的用例。那时还不知道其实已经有 pytest-parallel 这个支持多线程运行用例的插件了。它们两个的对比如下:文章源自玩技e族-https://www.playezu.com/240315.html

1.pytest 的-x 参数失效了。pytest 原有的功能如果输入-x 参数时,如果有一个用例失败时,pytest 会立刻停止运行。但是启用了这两个插件后,pytes 会运行完所有用例。文章源自玩技e族-https://www.playezu.com/240315.html

2.启用这两个插件后,allure-pytest 生成的测试报告将无效文章源自玩技e族-https://www.playezu.com/240315.html

3.pytest-multithreading 比 pytest-parallel 有一个优点。pytest-paralle 运行 session 级别的 fixture。每个线程都会运行一次。这是和 pytest-xdist 同样的问题。而且如果 session 级别的 fixture 是有 IO 等待的话。pytest-paralle 执行用例的总时长将会大大增加文章源自玩技e族-https://www.playezu.com/240315.html

总结

虽然 pytest-multithreading 有以上缺点,但是我还是觉得它对我的工作是很有帮助的。因为我有几千个有 IO 等待的用例需要经常运行。这个时候 pytest-multithreading 能够最快的帮我运行完。总结起来说就是 pytest-multithreading 的适用场景是有很多有 IO 等待的用例需要快速运行。以后再找时间兼容下 allure-pytest 插件和-x 参数的问题文章源自玩技e族-https://www.playezu.com/240315.html

 
评论  14  访客  14
    • yuewei
      yuewei 9

      大佬 一直报这个错 辛苦帮忙看看

      • test
        test 9

        嗨,多线程运行用例,用例就是可能交叉执行的,你想用例不允许交叉执行,就不能用多线程了

        • 古一
          古一 9

          hi,一运行报这个错,python3.9,能帮看看吗

          • 测试小书童
            测试小书童 9

            使用了pytest-multithreading 发现个问题,pytest-html上的记录日志,会被打乱,我这样调用:
            pytest -s testcase/ –th 10 –html=report.html –self-contained-html –capture=sys

          匿名

          发表评论

          匿名网友
          确定

          拖动滑块以完成验证