Подтвердить что ты не робот

Использование python mock patch.object для изменения возвращаемого значения метода, вызванного другим методом

Можно ли смоделировать возвращаемое значение функции, вызываемой внутри другой функции, которую я пытаюсь проверить? Я хотел бы, чтобы mocked-метод (который будет вызываться во многих тестируемых мной методах) возвращал мои указанные переменные при каждом вызове. Например:

class Foo:
    def method_1():
       results = uses_some_other_method()
    def method_n():
       results = uses_some_other_method()

В модульном тесте я хотел бы использовать mock, чтобы изменить значение, возвращаемое с помощью uses_some_other_method() чтобы при каждом вызове в Foo он возвращал то, что я определил в @patch.object(...)

4b9b3361

Ответ 1

Есть два способа сделать это; с патчем и с patch.object

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

#foo.py
def some_fn():
    return 'some_fn'

class Foo(object):
    def method_1(self):
        return some_fn()

#bar.py
import foo
class Bar(object):
    def method_2(self):
        tmp = foo.Foo()
        return tmp.method_1()

#test_case_1.py
import bar
from mock import patch

@patch('foo.some_fn')
def test_bar(mock_some_fn):
    mock_some_fn.return_value = 'test-val-1'
    tmp = bar.Bar()
    assert tmp.method_2() == 'test-val-1'
    mock_some_fn.return_value = 'test-val-2'
    assert tmp.method_2() == 'test-val-2'

Если вы напрямую импортируете тестируемый модуль, вы можете использовать patch.object следующим образом:

#test_case_2.py
import foo
from mock import patch

@patch.object(foo, 'some_fn')
def test_foo(test_some_fn):
    test_some_fn.return_value = 'test-val-1'
    tmp = foo.Foo()
    assert tmp.method_1() == 'test-val-1'
    test_some_fn.return_value = 'test-val-2'
    assert tmp.method_1() == 'test-val-2'

В обоих случаях some_fn будет "un-mocked" после завершения тестовой функции.

Изменить: Чтобы издеваться над несколькими функциями, просто добавьте больше декораторов в функцию и добавьте аргументы, чтобы взять дополнительные параметры

@patch.object(foo, 'some_fn')
@patch.object(foo, 'other_fn')
def test_foo(test_other_fn, test_some_fn):
    ...

Обратите внимание, что чем ближе декоратор к определению функции, тем раньше он находится в списке параметров.

Ответ 2

Это можно сделать примерно так:

# foo.py
class Foo:
    def method_1():
        results = uses_some_other_method()


# testing.py
from mock import patch

@patch('Foo.uses_some_other_method', return_value="specific_value"):
def test_some_other_method(mock_some_other_method):
    foo = Foo()
    the_value = foo.method_1()
    assert the_value == "specific_value"

Вот источник, который вы можете прочитать: исправления в неправильном месте

Ответ 3

Позвольте мне прояснить, о чем вы говорите: вы хотите протестировать Foo в тестовом примере, который вызывает внешний метод uses_some_other_method. Вместо того, чтобы вызывать реальный метод, вы хотите смоделировать возвращаемое значение.

class Foo:
    def method_1():
       results = uses_some_other_method()
    def method_n():
       results = uses_some_other_method()

Хорошо, предположим, что приведенный выше код находится в foo.py, uses_some_other_method в модуле bar.py Вот тестовый блок:

import unitest
import mock

from foo import Foo


class TestFoo(unittest.TestCase):

    def setup(self):
        self.foo = Foo()

    @mock.patch('foo.uses_some_other_method')
    def test_method_1(self, mock_method):
        mock_method.return_value = 3
        self.foo.method_1(*args, **kwargs)

        mock_method.assert_called_with(*args, **kwargs)

Если вы хотите изменять возвращаемое значение каждый раз, когда передаете различные аргументы, mock предоставляет side_effect.