2014-09-29 2 views
2

Я написал класс, который наследует класс многопроцессорности .Process(). В инициализации я устанавливаю некоторые параметры, один из них - это другой класс, который записывает в файл на моем жестком диске. Для модульного тестирования я хотел бы высмеять этот экземпляр класса, чтобы избежать записи в файл. Вот несколько минимальных примеров:Класс Python наследует многопроцессорность: издевается над одним из объектов класса

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() 

Код, указанный выше, должен работать как есть. Однако, если я попытаюсь высмеять класс _some_class в классе MyProcess (= раскомментирование строки в основной функции) Я получаю ошибки. Интересно, что я получаю то же самое, если пытаюсь инициализировать self._some_class с функцией (например, заменить строку 13 в коде выше с помощью * self._some_class = lambda x: x/2 *). Поэтому я предполагаю, что есть некоторая проблема с копированием объектов в MyProcess при возникновении нового процесса. Это вызывает два вопроса:

  • Может ли кто-нибудь пролить свет, почему невозможно инициализировать объект класса с помощью функции?
  • Как я мог издеваться над одним из объектов класса MyProcess?

Я был бы очень признателен за любую помощь ...

EDIT 1 (больше информации о сообщениях об ошибках):

Если я раскомментировать строку в главной функции я получаю кучу ошибок, где я думаю, что должна быть соответствующая одна следующее:

pickle.PicklingError: не можете замариновать: это не тот же объект mock.Mock

EDIT 2 (нашел соответствующую информацию):

Я нашел следующее issue в коде Google, что, похоже, связано с моей проблемой. Действительно, изменение издеваются в основной функции к следующему делает код, исполняемый:

proc._some_class = mock.MagicMock() 
proc._some_class.__class__ = mock.MagicMock 

Однако то, что я был бы заинтересован в для тестирования следующий вызов: proc._some_class.some_method.called, который всегда False, несмотря на то, что метод, очевидно, был вызван. Я предполагаю, что это имеет какое-то отношение к обходному пути, о котором я говорил выше.

EDIT 3 (обходной путь основан на предложении JB.):

можно обойти эту проблему, вызвав запустить метод напрямую. Следующий код содержит основную функцию и показывает, как проверить функцию с помощью издевается:

if __name__ == '__main__': 

    proc = MyProcess() 
    proc._some_class = mock.MagicMock() 
    proc.run() 

    print(proc._some_class.sum.called) 
+0

... какие ошибки? – jonrsharpe

+0

Я получаю полный список ошибок, но я бы предположил, что соответствующий должен быть: * pickle.PicklingError: не может pickle : это не тот же объект, что и mock.Mock * – Chris

+0

@Veedrac : done, я переместил информацию с моего комментария на исходный пост. – Chris

ответ

1

Хотя это не Ваш вопрос напрямую, пожалуйста, рассмотрите следующий подход. Наследование объекта Process может упростить реализацию, но (как вы заметили) может быть очень сложным при выполнении модульного тестирования.

Было бы намного проще, если вы передаете run функцию в качестве параметра Process, например, в том, как вы можете проверить run функцию отдельно от многопроцессорной enviorment. Если вам нужно протестировать поведение 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, интерфейс питон многопроцессорная опирается на pickling и макет библиотеки и травление не идут well together , Просто напишите свой собственный насмешливый класс.

+0

Спасибо за это предложение. Я решил не менять сам дизайн, а просто получить доступ к функции запуска моего класса напрямую, не создавая новый процесс. Я добавлю обходной путь к своему оригинальному сообщению. – Chris

Смежные вопросы