我编写了一个继承 multiprocessing.Process() 类的类。在初始化中,我设置了一些参数,其中一个是写入硬盘驱动器上某个文件的另一个类。出于单元测试的目的,我想模拟此类实例以避免实际写入某个文件。这是一些最小的例子:

import mock
import time
import multiprocessing as mp
import numpy as np

class MyProcess(mp.Process):

    def __init__(self):
        super(MyProcess, self).__init__()

        # the original code would create some instance of a file manipulation
        # class here:
        self._some_class = np.zeros(100)

    def run(self):

        # the following line would actually write to some file in the original
        # code:
        self._some_class.sum()

        for ii in range(10):
            print(str(ii))
            time.sleep(.01)

if __name__ == '__main__':

    proc = MyProcess()
#     proc._some_class = mock.Mock()

    proc.start()
    proc.join()

上面的代码应该按原样运行。但是,如果我尝试模拟 MyProcess 类中的 _some_class 类(= 取消注释 main 函数中的行),则会出现错误。有趣的是,如果我尝试使用函数初始化 self._some_class,我会得到完全相同的错误(例如,将上面代码中的第 13 行替换为 *self._some_class = lambda x: x/2 *)。所以我的猜测是在产生新进程时复制 MyProcess 中的对象存在一些问题。这就提出了两个问题:
  • 有人能解释一下为什么不能用函数初始化类对象吗?
  • 如何模拟 MyProcess 的类对象之一?

  • 我真的很感激任何帮助......

    编辑 1(有关错误消息的更多信息):

    如果我取消注释 main 函数中的行,我会收到一堆错误,我认为以下应该是相关的错误:

    pickle.PicklingError: Can't pickle : 它与 mock.Mock 不是同一个对象

    编辑2(找到一些相关信息):

    我在谷歌代码上发现了以下issue,这似乎与我的问题有关。实际上,将主函数中的模拟更改为以下内容会使代码可执行:
    proc._some_class = mock.MagicMock()
    proc._some_class.__class__ = mock.MagicMock
    

    但是,我对测试感兴趣的是以下调用:proc._some_class.some_method.call,尽管该方法显然已被调用,但它始终为 False。我想这与我上面提到的解决方法有关。

    编辑 3(基于 jb 的建议的解决方法。):

    可以通过直接调用 run 方法来解决这个问题。以下代码包含 main 函数并展示了如何使用模拟测试该函数:
    if __name__ == '__main__':
    
        proc = MyProcess()
        proc._some_class = mock.MagicMock()
        proc.run()
    
        print(proc._some_class.sum.called)
    

    最佳答案

    虽然这不能直接解决您的问题,但请考虑以下方法。从 Process 对象继承可能会使实现更容易,但是(正如您所指出的)在进行单元测试时可能非常困难。

    如果您将 run 函数作为参数传递给 Process 实例,那么您可以将 run 函数与多处理环境分开测试,这样会容易得多。如果您需要在另一个进程中测试 run 行为,只需创建一个可调用对象,并在其中模拟适当的内容。

    沿线的东西:

    from multiprocessing import Process
    
    def f(name):
        print 'hello', name
    
    if __name__ == '__main__':
        p = Process(target=f, args=('bob',))
        p.start()
        p.join()
    

    如果你想坚持你的设计,你不应该使用 MagicMock,python 多处理接口(interface)依赖于 pickling ,而模拟库和酸洗不要去 well together 。只需编写自己的模拟类。

    关于继承多处理 : mocking one of the class objects 的 Python 类,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26094560/

    10-11 22:43
    查看更多