Python Mocking функція із імпортного модуля


125

Я хочу зрозуміти, як @patchфункціонувати з імпортованого модуля.

Ось де я поки що.

app / mocking.py:

from app.my_module import get_user_name

def test_method():
  return get_user_name()

if __name__ == "__main__":
  print "Starting Program..."
  test_method()

app / my_module / __ init__.py:

def get_user_name():
  return "Unmocked User"

тест / mock-test.py:

import unittest
from app.mocking import test_method 

def mock_get_user():
  return "Mocked This Silly"

@patch('app.my_module.get_user_name')
class MockingTestTestCase(unittest.TestCase):

  def test_mock_stubs(self, mock_method):
    mock_method.return_value = 'Mocked This Silly')
    ret = test_method()
    self.assertEqual(ret, 'Mocked This Silly')

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

Це не працює, як я б очікував. "Виправлений" модуль просто повертає розблоковане значення get_user_name. Як я знущаюся над методами з інших пакетів, які я імпортую в тестову область імен?


1
Питання стосується "глузування з найкращих практик" чи має сенс те, що ви робите? Щодо першого, я б сказав використовувати Mockглузуючу бібліотеку типу , яка включена в python3.3 + as unittest.mock.
Бакуріу

Я запитую, чи я буду робити це правильно. Я подивився на Мока, але не бачу способу вирішити цю конкретну проблему. Чи є спосіб відтворити те, що я робив вище в Mock?
nsfyn55

Відповіді:


167

Коли ви використовуєте patchдекоратор з unittest.mockпакету, ви не виправляєте простір імен, з якого імпортується модуль (у цьому випадку app.my_module.get_user_name), ви виправляєте його в тестовій області імен app.mocking.get_user_name.

Щоб виконати вище, Mockспробуйте щось на кшталт наведеного нижче:

from mock import patch
from app.mocking import test_method 

class MockingTestTestCase(unittest.TestCase):

    @patch('app.mocking.get_user_name')
    def test_mock_stubs(self, test_patch):
        test_patch.return_value = 'Mocked This Silly'
        ret = test_method()
        self.assertEqual(ret, 'Mocked This Silly')

Стандартна бібліотечна документація містить корисний розділ, що описує це.


це потрапляє до моєї проблеми. get_user_nameзнаходиться в іншому модулі, ніж test_method. Чи є спосіб знущатися над чим-небудь у підмодулі? Я виправив це потворно внизу.
nsfyn55

6
Не важливо, що get_user_nameце в іншому модулі, ніж те, що test_methodви імпортуєте функцію в app.mockingних, вони знаходяться в одному просторі імен.
Матті Джон

2
Звідки взявся test_patch, що це саме?
Майк Г

2
test_patch передається декоратором патчу і є знущаним об’єктом get_user_name (тобто екземпляром класу MagicMock). Це може бути зрозуміліше, якби його назвали чимось на кшталт get_user_name_patch.
Матті Джон

Як ви посилаєтеся на test_method? Це призведе до помилки, NameError: глобальна назва 'test_method' не визначено
Aditya

12

Хоча відповідь Матті Джона вирішує вашу проблему (і мені теж допомогла, дякую!), Я б запропонував локалізувати заміну оригінальної функції "get_user_name" на знущену функцію. Це дозволить вам контролювати, коли функція замінена та коли її немає. Також це дозволить вам зробити кілька замін в одному тесті. Для цього використовуйте статтю "з" досить подібним чином:

from mock import patch

class MockingTestTestCase(unittest.TestCase):

    def test_mock_stubs(self):
        with patch('app.mocking.get_user_name', return_value = 'Mocked This Silly'):
            ret = test_method()
            self.assertEqual(ret, 'Mocked This Silly')

6
Це щось несуттєве для поставленого питання. Незалежно від того, чи використовуєте ви його patchяк декоратор чи контекстний менеджер, характерно для випадку використання. Наприклад, ви можете використовувати patchяк декоратор, щоб висміювати значення для всіх тестів у xunitабо pytestкласі, а в інших випадках корисно мати контрольований дрібнозернистий контроль, наданий менеджером контексту.
nsfyn55
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.