简体   繁体   English

如何使用pytest-mock检查单元测试中是否调用了函数?

[英]How to check if a function was called in a unit test using pytest-mock?

I have a unit test where I want to check if a function was called.我有一个单元测试,我想检查一个函数是否被调用。 How do I do this with pytest and pytest-mock libraries?如何使用pytestpytest-mock库执行此操作?

For example, here is a unit test test_hello.py .例如,这是一个单元测试test_hello.py In this test I call the function my_function and want to verify that it called hello with a given argument.在这个测试中,我调用函数my_function并想验证它是否使用给定的参数调用了hello

def hello(name):
    return f'Hello {name}'

def my_function():
    hello('Sam')

def test_hello(mocker):
    mocker.patch('hello')
    my_function()
    hello.assert_called_once_with('Sam')

The code above returns the following error:上面的代码返回以下错误:

target = 'hello'

    def _get_target(target):
        try:
>           target, attribute = target.rsplit('.', 1)
E           ValueError: not enough values to unpack (expected 2, got 1)

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py:1393: ValueError

During handling of the above exception, another exception occurred:

mocker = <pytest_mock.MockFixture object at 0x109c5e978>

    def test_hello(mocker):
>       mocker.patch('hello')

test_hello.py:8: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pytest_mock.py:156: in __call__
    return self._start_patch(self.mock_module.patch, *args, **kwargs)
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pytest_mock.py:134: in _start_patch
    p = mock_func(*args, **kwargs)
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py:1544: in patch
    getter, attribute = _get_target(target)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

target = 'hello'

    def _get_target(target):
        try:
            target, attribute = target.rsplit('.', 1)
        except (TypeError, ValueError):
            raise TypeError("Need a valid target to patch. You supplied: %r" %
>                           (target,))
E           TypeError: Need a valid target to patch. You supplied: 'hello'

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py:1396: TypeError

Solving the error by 通过解决错误

assign a mocked_hello to mocked.patch mocked_hello分配给mocked.patch

assign a side_effect to mocked func 为mocked func分配side_effect

def bonjour(name):
    return 'bonjour {}'.format(name)


def hello(name):
    return 'Hello {}'.format(name)


def my_function():
   return hello('Sam')


def test_hellow_differnt_from_module(mocker):
    # mocked func with `test_hello.py` as module name
    mocked_hello = mocker.patch('test_hello.hello')
    # assign side_effect to mocked func
    mocked_hello.side_effect = bonjour
    # the mocked func return_value changed by side_effect
    assert mocked_hello('Sam') == 'bonjour Sam'
    # the mocked func called with Sam, but with different return value
    mocked_hello.assert_called_with('Sam')

call a real function my_function() and the verify that it called hello 调用一个真正的函数my_function()并验证它是否调用了hello

def test_my_function(mocker):
    mocker.patch('test_hello.hello', side_effect=bonjour)
    mf = my_function()
    hello.assert_called_with('Sam')
    assert mf == 'bonjour Sam'

I think the minimal change to the OP's code that would make it work would be simply to fully qualify the name of the mocked function.我认为对 OP 代码进行的最小更改将使其工作只是完全限定模拟函数的名称。 So, for example, if the code is in a file called test.py :因此,例如,如果代码位于名为test.py的文件中:

import pytest

def hello(name):
    return f'Hello {name}'

def my_function():
    hello('Sam')

def test_hello(mocker):
    mocker.patch('test.hello')
    my_function()
    hello.assert_called_once_with('Sam')

(I'm assuming test.py is located somewhere in sys.path .) (我假设test.py位于sys.path某处。)

However, I think it's better to change the test function to be:但是,我认为最好将测试功能更改为:

def test_hello(mocker):
    mocked_func = mocker.patch('test.hello')
    my_function()
    mocked_func.assert_called_once_with('Sam')

That form seems clearer to me and is less likely to raise an AttributeError in more complex code.这种形式对我来说似乎更清晰,并且不太可能在更复杂的代码中引发 AttributeError 。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM