是否可以控制pytest-xdist如何并行运行测试?

54 投票
6 回答
28321 浏览
提问于 2025-04-16 09:38

我有以下的目录结构:

runner.py
lib/
tests/
      testsuite1/
                 testsuite1.py
      testsuite2/
                 testsuite2.py
      testsuite3/
                 testsuite3.py
      testsuite4/
                 testsuite4.py

testsuite*.py模块的格式如下:

import pytest 
class testsomething:
      def setup_class(self):
          ''' do some setup '''
          # Do some setup stuff here      
      def teardown_class(self):
          '''' do some teardown'''
          # Do some teardown stuff here

      def test1(self):
          # Do some test1 related stuff

      def test2(self):
          # Do some test2 related stuff

      ....
      ....
      ....
      def test40(self):
          # Do some test40 related stuff

if __name__=='__main()__'
   pytest.main(args=[os.path.abspath(__file__)])

我遇到的问题是,我想要并行执行这些'testsuites',也就是说,我希望testsuite1、testsuite2、testsuite3和testsuite4能够同时开始执行,但每个testsuite内部的测试需要依次执行。

当我使用py.test的'xdist'插件,并通过'py.test -n 4'来启动测试时,py.test会收集所有的测试,并随机将这些测试分配给4个工作进程。这导致'testsuitex.py'模块中的每个测试都要每次都执行'setup_class'方法(这正好与我的目的相悖。我希望'setup_class'只在每个类中执行一次,之后的测试依次执行)。

我希望的执行方式是:

worker1: executes all tests in testsuite1.py serially
worker2: executes all tests in testsuite2.py serially
worker3: executes all tests in testsuite3.py serially
worker4: executes all tests in testsuite4.py serially

同时执行 worker1, worker2, worker3 和 worker4

有没有办法在'pytest-xdist'框架中实现这个目标?

我能想到的唯一选项是启动不同的进程,在runner.py中分别执行每个测试套件:


def test_execute_func(testsuite_path):
    subprocess.process('py.test %s' % testsuite_path)

if __name__=='__main__':
   #Gather all the testsuite names
   for each testsuite:
       multiprocessing.Process(test_execute_func,(testsuite_path,))

6 个回答

15

目前,使用pytest-xdist时,并没有“按文件”或“按测试套件”来分配测试的功能。实际上,如果你希望每个文件的测试(比如一个文件里的测试最多只能被一个工作者同时执行)能帮助到你的情况,我建议你去pytest的问题追踪器提交一个功能请求,可以在这里找到:https://bitbucket.org/hpk42/pytest/issues?status=new&status=open,并把你在这里的详细解释链接过去。

祝好,holger

24

可用的多种选项

是的,有这样的方式,针对版本 1.28.0 的 xdist,提供了以下几种选项:

  • --dist=each: 将所有测试发送到所有节点,这样每个测试都会在每个节点上运行一次。
  • --dist=load: 在所有节点之间分配收集到的测试,这样每个测试只会运行一次。所有节点会收集并提交测试套件,当所有的测试套件都收集完后,会检查它们是否完全相同。然后,这些测试会被分成小块,分配给节点去执行。
  • --dist=loadscope: 在所有节点之间分配收集到的测试,这样每个测试只会运行一次。所有节点会收集并提交测试列表,当所有的列表都收集完后,会检查它们是否完全相同。然后,这些测试会根据测试范围分成工作单元,再将这些工作单元分配给节点。
  • --dist=loadfile: 在所有节点之间分配收集到的测试,这样每个测试只会运行一次。所有节点会收集并提交测试列表,当所有的列表都收集完后,会检查它们是否完全相同。然后,这些测试会根据测试文件分成工作单元,再将这些工作单元分配给节点。

如果你需要更多信息,建议直接查看 调度器的实际实现,了解分配是如何进行的。

33

你可以使用 --dist=loadscope 来把同一个测试类里的所有测试放在一起。这里有来自 pytest-xdist 在 pypi 的文档

默认情况下,-n 选项会把待执行的测试分配给任何可用的工作者,顺序是没有保证的,但你可以用以下选项来控制:

--dist=loadscope:测试会按模块分组(对于测试函数)和按类分组(对于测试方法),然后每个组会被发送到一个可用的工作者,这样可以确保同一组里的所有测试都在同一个进程中运行。如果你的模块级或类级的准备工作比较复杂,这个功能会很有用。目前,分组方式不能自定义,按类分组的优先级高于按模块分组。这项功能是在 1.19 版本中添加的。

--dist=loadfile:测试会按文件名分组,然后会被发送到一个可用的工作者,确保同一组里的所有测试都在同一个工作者中运行。这项功能是在 1.21 版本中添加的。

撰写回答