Python Mock Patch несколько методов в классе

Я пытаюсь исправить несколько методов в классе. Вот моя упрощенная установка

Hook.py определяется как

class Hook():
    def get_key(self):
        return "Key"

    def get_value(self):
        return "Value"

HookTransfer.py определяется как

from Hook import Hook

class HookTransfer():
    def execute(self):
        self.hook = Hook()
        key = self.hook.get_key()
        value = self.hook.get_value()
        print(key)
        print(value)

Я хочу издеваться над методами get_key и get_value в классе Hook. Следующие работы, т.е. печатают New_Key и New_Value

from HookTransfer import HookTransfer
import unittest
from unittest import mock

class TestMock(unittest.TestCase):
    @mock.patch('HookTransfer.Hook.get_key', return_value="New_Key")
    @mock.patch('HookTransfer.Hook.get_value', return_value="New_Value")
    def test_execute1(self, mock_get_key, mock_get_value):
        HookTransfer().execute()

if __name__ == '__main__':
    unittest.main()

Однако это не так. Он печатает <MagicMock name='Hook().get_key()' id='4317706896'> и <MagicMock name='Hook().get_value()' id='4317826128'>

from HookTransfer import HookTransfer
import unittest
from unittest import mock

class TestMock(unittest.TestCase):
    @mock.patch('HookTransfer.Hook', spec=True)
    def test_execute2(self, mock_hook):
        mock_hook.get_key = mock.Mock(return_value="New_Key")
        mock_hook.get_value = mock.Mock(return_value="New_Value")
        HookTransfer().execute()

if __name__ == '__main__':
    unittest.main()

Интуитивно кажется, что второй тоже должен работать, но это не так. Не могли бы вы помочь объяснить, почему это не так. Я подозреваю, что это как-то связано с "где патчить" но я не могу получить ясность.


person kvb    schedule 13.01.2017    source источник
comment
Тут что-то нелогично... В вашем первом примере вы import HookTransfer (кажется, это имя модуля), но потом вы вызываете модуль в тесте (как если бы у вас было from HookTransfer import HookTransfer) .   -  person mgilson    schedule 13.01.2017
comment
Я вручную написал код ранее, потому что он был частью гораздо большего файла. Я специально протестировал и вставил новый код выше. Результат тот же. Спасибо, что указали.   -  person kvb    schedule 13.01.2017


Ответы (3)



Что вам нужно:

издеваться над классом Крюк,

from HookTransfer import HookTransfer
from Hook import Hook

import unittest
try:
    import mock
except ImportError:
    from unittest import mock

class TestMock(unittest.TestCase):
    @mock.patch.object(Hook, 'get_key', return_value="New_Key")
    @mock.patch.object(Hook, 'get_value', return_value="New_Value")
    def test_execute1(self, mock_get_key, mock_get_value):
        HookTransfer().execute()

if __name__ == "__main__":
    unittest.main()
person John He    schedule 08.04.2017

После некоторого тестирования я смог найти проблему.

Во втором тестовом примере декоратор патча создает новый экземпляр класса Mock и передает его через аргумент mock_hook функции test_execute2. Давайте назовем это mock1. mock1 заменяет класс Hook в HookTransfer.py. Когда self.hook = Hook() запускается, это преобразуется в вызов __init__ из mock1. По дизайну это возвращает еще один экземпляр Mock — давайте назовем его mock2. Итак, self.hook указывает на mock2. Но mock_hook.get_key = mock.Mock(return_value="New_Key") издевается над методами mock1.

Для корректного мока необходимо пропатчить mock2. Это можно сделать 2 способами

  1. Издеваясь над return_value mock1 (который возвращает mock2) mock_hook.return_value.get_key = mock.Mock(return_value="New_Key")
  2. Имитация возвращаемого значения конструктора mock1 (который возвращает mock2) mock_hook().get_key = mock.Mock(return_value="New_Key")

На самом деле оба варианта делают одно и то же.

person kvb    schedule 15.01.2017