Макет юниттеста Python:Можно ли высмеивать значение аргументов метода по умолчанию во время тестирования?

StackOverflow https://stackoverflow.com//questions/24021491

  •  21-12-2019
  •  | 
  •  

Вопрос

У меня есть метод, который принимает аргументы по умолчанию:

def build_url(endpoint, host=settings.DEFAULT_HOST):
    return '{}{}'.format(host, endpoint)

У меня есть тестовый пример, который реализует этот метод:

class BuildUrlTestCase(TestCase):
    def test_build_url(self):
        """ If host and endpoint are supplied result should be 'host/endpoint' """

        result = build_url('/end', 'host')
        expected = 'host/end'

        self.assertEqual(result,expected)

     @patch('myapp.settings')
     def test_build_url_with_default(self, mock_settings):
        """ If only endpoint is supplied should default to settings"""
        mock_settings.DEFAULT_HOST = 'domain'

        result = build_url('/end')
        expected = 'domain/end'

        self.assertEqual(result,expected)

Если я удалю точку отладки build_url и проверьте этот атрибут settings.DEFAULT_HOST возвращает искажённое значение.Однако тест по-прежнему терпит неудачу, и утверждение указывает host присваивается значение из моего фактического settings.py.Я знаю, это потому, что host Аргумент ключевого слова устанавливается во время импорта, и мой макет не учитывается.

отладчик

(Pdb) settings
<MagicMock name='settings' id='85761744'>                                                                                                                                                                                               
(Pdb) settings.DEFAULT_HOST
'domain'
(Pdb) host
'host-from-settings.com'                                                                                                                                                 

Есть ли способ переопределить это значение во время тестирования, чтобы я мог использовать путь по умолчанию с помощью издеваемого settings объект?

Это было полезно?

Решение

Функции хранят значения своих параметров по умолчанию в func_defaults атрибут, когда функция определена, так что вы можете исправить это.Что-то вроде

def test_build_url(self):
    """ If only endpoint is supplied should default to settings"""

    # Use `func_defaults` in Python2.x and `__defaults__` in Python3.x.
    with patch.object(build_url, 'func_defaults', ('domain',)):
      result = build_url('/end')
      expected = 'domain/end'

    self.assertEqual(result,expected)

я использую patch.object в качестве контекстного менеджера, а не декоратора, чтобы избежать передачи ненужного объекта патча в качестве аргумента test_build_url.

Другие советы

Я применил другой ответ на этот вопрос, но после контекстного менеджера пропатченная функция стала не такой, как раньше.

Моя исправленная функция выглядит так:

def f(foo=True):
    pass

В своем тесте я сделал это:

with patch.object(f, 'func_defaults', (False,)):

При звонке f после (не в) контекстного менеджера значение по умолчанию полностью исчезло, а не вернулось к предыдущему значению.Вызов f без аргументов выдал ошибку TypeError: f() takes exactly 1 argument (0 given)

Вместо этого я просто сделал это перед тестом:

f.func_defaults = (False,)

И это после моего теста:

f.func_defaults = (True,)

Альтернативный способ сделать это:Используйте functools.partial, чтобы предоставить нужные аргументы «по умолчанию».Это не технически то же самое, что и их переопределение;вызывающая сторона видит явный аргумент, но вызывающая сторона не обязана его предоставлять.В большинстве случаев это достаточно близко, и после выхода из контекстного менеджера это происходит правильно:

# mymodule.py
def myfunction(arg=17):
    return arg

# test_mymodule.py
from functools import partial
from mock import patch

import mymodule

class TestMyModule(TestCase):
    def test_myfunc(self):
        patched = partial(mymodule.myfunction, arg=23)
        with patch('mymodule.myfunction', patched):
            self.assertEqual(23, mymodule.myfunction())  # Passes; default overridden
        self.assertEqual(17, mymodule.myfunction()) # Also passes; original default restored

Я использую это для переопределения расположения файлов конфигурации по умолчанию при тестировании.Спасибо, где нужно, я получил идею от Данило Баргена. здесь

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top