与多个进程并行运行测试

备注

0.3版新增功能

使用 mp 插件,用于跨多个进程分发测试。如果测试严重限制了IO或CPU,那么这样做可能会加快测试运行。但是 征收间接费用 这不是小事,而且 使测试夹具的使用复杂化 5月 与未设计用于它的插件冲突 .

使用

要激活插件,请将插件模块包含在插件列表中 [unittest] 配置文件中的节::

[unittest]
plugins = nose2.plugins.mp

或通过模块 --plugin 命令行选项:

nose2 --plugin=nose2.plugins.mp

然后配置要运行的进程数。你也可以用 -N 选项:

nose2 -N 2

或通过设置 processes[multiprocess] 配置文件的节::

[multiprocess]
processes = 2

备注

如果通过设置使插件始终处于活动状态 always-on[multiprocess] 配置文件的节,但不设置 processes 或通行证 -N ,进程数默认为可用CPU数。还要注意,值0将把实际进程数设置为计算机上的CPU数。

如果要指定进程间通信使用Internet套接字,请指定 bind_address 设置在 [multiprocess] 配置文件的节,例如:

[multiprocess]
bind_address = 127.0.0.1:1024

这将绑定到的端口1024 127.0.0.1 . 也::

[multiprocess]
bind_address = 127.1.2.3

将绑定到上的任意打开端口 127.1.2.3 . python可以识别的任何Internet地址或主机名,bind, and 可接受连接。同时 0.0.0.0 可以用于监听,它不一定是操作系统可以连接到的地址。当端口地址为 0 或者省略,使用随机开放端口。如果该设置被省略或为空,则除非在窗口上执行鼻形操作,否则不使用插座。在这种情况下,将使用回送接口上的地址和随机端口。无论何时使用,进程都使用随机共享密钥进行身份验证。

测试作者指南

当并行运行时,并不是每个测试套件都能很好地工作,或者根本不能工作。对于某些测试套件,并行执行毫无意义。对于其他人,它将在测试用例和测试模块中公开bug和排序依赖项。

间接费用

启动子流程和分派测试需要时间。一种测试运行,其中包含数量相对较少的不受I/O或CPU限制的测试(或调用 time.sleep() )很可能是 更慢的 平行运行时。

例如,在撰写本文时,nose2的测试套件的运行时间大约是使用时的10倍。 multiprocessing 因为间接费用。

共享设备

单个测试过程在启动后不共享状态或数据。这意味着 共享夹具的测试 --从模块加载的测试 setUpModule 定义,并在定义 setUpClass —— 必须同时将所有文件发送到同一进程 . 因此,如果您使用这些类型的设备,那么您的测试运行的并行性可能会低于预期。

测试加载两次

测试用例可能不可酸洗,因此nose2不能将它们直接传输到其测试运行程序进程。测试按名称分发。这意味着 测试总是加载两次 --在主进程中,在初始收集期间,然后在测试运行程序进程中,按名称加载它们。对于某些测试套件来说,这可能有问题。

随机执行顺序

并行运行时,测试的执行顺序不同。结果将以有效的随机顺序返回,并在同一模块中进行测试。( 只要他们不共用固定装置 )可以以任何顺序和不同的过程执行。有些测试套件有顺序依赖关系,不管是有意的还是无意的,当使用这个插件运行时,这些依赖关系将随机失败。

插件作者指南

多进程插件被设计为与其他插件一起工作,但是其他插件可能必须返回支持,特别是当它们加载测试或关心发生的事情时 在期间 测试执行。

新方法

这个 MultiProcess plugin添加了一些插件钩子,其他插件可以使用这些钩子为多进程测试运行设置自己。插件不必做任何特殊的事情来注册这些钩子;只需像平常一样实现这些方法。

registerInSubprocess(self, event)
参数:

event -- nose2.plugins.mp.RegisterInSubprocessEvent

这个 registerInSubprocess 在插件注册之后调用hook,以使需要在子流程中运行的插件能够注册该事实。对于需要在子流程中运行的插件,最常见的做法是:

def registerInSubprocess(self, event):
    event.pluginClasses.append(self.__class__)

插件不需要附加自己的类。如果出于某种原因,有一个不同的插件类或一组类应该在运行子流程的测试中运行,则添加该类或那些类。

startSubprocess(self, event)
参数:

event -- nose2.plugins.mp.SubprocessEvent

这个 startSubprocess 钩子在每个运行子进程的测试加载插件之后,但在执行任何测试之前激发。

插件可以在这里以与中相同的方式自定义测试执行 startTestRun() ,通过设置 event.executeTests ,并通过设置阻止测试执行 event.handled 对真,对假。

stopSubprocess(self, event)
参数:

event -- nose2.plugins.mp.SubprocessEvent

这个 stopSubprocess 事件在每个运行子进程的测试关闭之前激发。插件可以使用这个钩子完成每个进程可能需要完成的任何工作。

将同一事件实例传递给 startSubprocessstopSubprocess 如果需要的话,插件可以使用该事件的元数据来从开始到停止钩子传递状态或其他信息。

新事件

这个 MultiProcess 插件的新挂钩随自定义事件类一起提供。

class nose2.plugins.mp.RegisterInSubprocessEvent(**metadata)[源代码]

激发事件以通知插件将发生多进程测试

pluginClasses

在此列表中添加一个插件类,以使插件在每个运行子进程的测试中被实例化。对于需要在子流程中运行的插件,最常见的做法是:

def registerInSubprocess(self, event):
    event.pluginClasses.append(self.__class__)
class nose2.plugins.mp.SubprocessEvent(loader, result, runner, plugins, connection, **metadata)[源代码]

在子进程执行的开始和结束时激发的事件。

loader

测试加载程序实例

result

测试结果

plugins

子进程中加载的插件列表。

connection

这个 multiprocessing.Connection 子进程用于与主进程通信的实例。

executeTests

将用于执行测试的可调用。插件可以将此属性设置为包装或以其他方式更改测试执行。可调用文件必须与签名匹配:

def execute(suite, result):
    ...

船尾警告

所有事件属性, including ``event.metadata``, must be pickleable . 如果您的插件设置了任何事件属性或将任何内容放入 event.metadata ,您有责任确保您可能放入的任何东西都是可腌制的。

我真的在乎吗?

如果你回答 yes 对于以下任何问题,如果不进行修改,您的插件将无法用于多进程测试:

  • 您的插件是否加载测试?

  • 您的插件是否捕获了测试执行期间发生的事情?

  • 您的插件在测试执行期间是否需要用户交互?

  • 您的插件是否在StartTestRun中设置了ExecuteTests?

下面介绍如何处理每一个案例。

加载测试

捕获测试执行状态

  • 实施 registerInSubprocess() 建议在测试运行程序进程中启用插件。

  • 小心设置 event.metadata 无条件地。您的插件将在主进程和测试运行程序进程中执行,并将看到 setTestOutcome()testOutcome() 事件 在两个过程中 . 如果您无条件地在 event.metadata ,主进程中的插件实例将覆盖子进程中该实例在该键中设置的任何内容。

  • 如果需要将某些内容写入文件,请实现 stopSubprocess() 在每个测试运行程序进程中写入一个文件。

重写测试执行

  • 实施 registerInSubprocess() 建议在测试运行程序进程中启用插件,并注意插件是在多进程会话下运行的。

  • 当运行多进程时, 设置 event.executeTests 在里面 startTestRun() --相反,把它放进去 startSubprocess() 相反。这将允许多进程插件在主进程中安装其测试执行器,而您的插件将接管测试运行程序子进程中的测试执行。

与用户交互

  • 你可能是安全的,因为作为一个负责任的插件作者,你已经启动了交互钩子。 (beforeInteraction()afterInteraction() )围绕你的互动位,并跳过它们当 beforeInteraction() 钩回 Falseevent.handled .

    如果你不这样做,就开始吧!

Windows上的可能问题

在windows上,有一些关于多处理的已知错误。

Windows上的Python不使用fork()。它从单独的解释器调用引导。在某些上下文中,参数的“值”将被视为“计数”,子流程使用它来构建命令行的标志。例如,如果此值为20亿(如散列种子),则subcess.py可能会尝试构建2GIG字符串,并可能抛出内存错误异常。与之相关的漏洞是http://bugs.python.org/issue20954.

参考文献

启用此插件

此插件是内置的,但默认情况下不加载。

即使您指定 always-on = True 在配置中,除非您也启用它,否则它不会运行。您可以通过将以下内容放入 unittest.cfgnose2.cfg 文件

[unittest]
plugins = nose2.plugins.mp

这个 plugins 参数可以包含插件名称列表,包括 nose2.plugins.mp

配置 [多进程]

always-on
违约:

类型:

布尔

bind_address
违约:

没有

类型:

STR

processes
违约:

0

类型:

整数

test-run-timeout
违约:

60.0

类型:

浮动

示例配置

默认配置相当于在 unittest.cfg 文件。

[multiprocess]
always-on = False
processes = 0
test-run-timeout = 60.0

命令行选项

-N DEFAULT, --processes DEFAULT

用于运行测试的进程数(0=自动)

插件类引用:多进程

class nose2.plugins.mp.MultiProcess(*args, **kwargs)[源代码]
property procs

如果self.procs为0,则为self.procs获取适当数量的procs。