在另一个模块中使用python,pytest,mock模拟第三方函数

时间:2018-07-12 18:03:53

标签: python unit-testing mocking pytest

我有一个正在生产的func1()函数,无法修改。它在另一个模块中调用函数function_to_be_mocked()。这需要输入参数。

我还有另一个函数func2(),它调用func1()。

我正在编写单元测试来测试func2(),并尝试模拟function_to_be_mocked(因为这取决于我本地系统上没有(也不应拥有)的某些键)。我唯一可以修改的是test_func2()。

我进行了如下设置(最小示例):

from othermodule import function_to_be_mocked
import pytest
import mock

def func1():
    function_to_be_mocked(None)

def func2():
    ret = func1()
    print (ret)

@mock.patch('othermodule.function_to_be_mocked', return_value = 3)
def test_func2(mocker):
    func2()

othermodule.py是:

def function_to_be_mocked(arg1):
    if not arg1 == 'foo':
        raise ValueError

我的输出:

直接调用func2:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/blah/temp.py", line 9, in func2
    ret = func1()
  File "/Users/blah/temp.py", line 6, in func1
    function_to_be_mocked(None)
  File "/Users/blah/othermodule.py", line 3, in function_to_be_mocked
    raise ValueError
ValueError

调用test_func2(),我希望它会被嘲笑:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/blah/venv/lib/python2.7/site-packages/mock/mock.py", line 1305, in patched
    return func(*args, **keywargs)
  File "/Users/blah/temp.py", line 14, in test_func2
    func2()
  File "/Users/blah/temp.py", line 9, in func2
    ret = func1()
  File "/Users/blah/temp.py", line 6, in func1
    function_to_be_mocked(None)
  File "/Users/blah/othermodule.py", line 3, in function_to_be_mocked
    raise ValueError
ValueError

因此,该模拟似乎无效。有没有人想到如何实现这一目标?

============在此行下编辑===========

听起来好像我无法做到我想做的(因为实际上我无法修改与功能1或功能2有关的任何内容。我所能控制的只是测试。

那么让我提出以下问题吧,也许比我的眼睛更有经验的眼睛看到前进的方向。

我有一个功能:

def function_to_be_tested(args):
    # Some processing steps

    # Function call that works locally
    function_that_returns_something_1()

    # Some logic

    # Function call that works locally
    function_that_returns_something_2()

    # Function that raises an exception when running locally,
    # and since I need to test the logic after this function 
    # (and cannot edit this code here to bypass it) I would 
    # (naively) like to mock it.
    function_I_would_like_to_mock()

    # Much more logic that follows this function. 
    # And this logic needs to be unit tested.
    return some_value_based_on_the_logic

测试:

def test_function_to_be_tested():
    assert function_to_be_tested(args) == some_expected_value

在function_I_would_like_to_mock()之前,我可以轻松地对任何内容进行单元测试。

但是由于此函数在本地崩溃(并且我无法编辑代码以阻止它在本地崩溃),所以我认为正确的方法是模拟它并强制使用有意义的返回值。这样我就可以对代码路径进行单元测试。

您会提出什么好的建议?

请注意,我唯一可以修改的是测试功能。我什至无法在主要功能中添加装饰器。

2 个答案:

答案 0 :(得分:2)

选项A)

您愿意模拟的函数已加载到func1中。因此,您必须将@patch装饰器应用于func1

import pytest
from unittest import mock

@mock.patch('othermodule.function_to_be_mocked', return_value = 3)
def func1(mocker):
    from othermodule import function_to_be_mocked
    function_to_be_mocked(None)

def func2():
    ret = func1()
    print (ret)

def test_func2():
    func2()

test_func2()

=========编辑==========

选项B)

import pytest
from unittest import mock


def func1():
    from othermodule import function_to_be_mocked
    function_to_be_mocked(None)

def func2():
    ret = func1()
    print (ret)

def test_func2():
    with mock.patch('othermodule.function_to_be_mocked', return_value = 3) as irrelevant:
        func2()

test_func2()

答案 1 :(得分:0)

“ unittest.mock-模拟对象库”官方文档的Where to patch部分对此进行了很清楚的解释:

a.py
    -> Defines SomeClass

b.py
    -> from a import SomeClass
    -> some_function instantiates SomeClass

现在,我们想测试some_function,但是我们想使用patch()模拟出SomeClass。问题在于,当我们导入模块b时(我们将必须执行此操作),它将从模块a导入SomeClass。如果我们使用patch()来模拟a.SomeClass,那么它将对我们的测试没有影响;模块b已经引用了真实的SomeClass,看来我们的修补没有任何作用。

关键是在使用(或查找位置)的地方修补SomeClass。在这种情况下,some_function实际上将在我们将其导入到的模块b中查找SomeClass。修补程序应如下所示:

@patch('b.SomeClass')

所以我认为在您的情况下,修补程序应如下所示:

@patch("module_of_func2.function_to_be_mocked_as_it_is_imported_there", return_value=3)
def test_func2():
    ...