Python测试: How to test correct function calls of downstream functions?

标签 python unit-testing testing mocking pytest

TLDR:使用错误的参数顺序调用上游函数。我如何确保测试能够捕获到这一点?

这是我的设置的一个最小示例:

# functions.py

def inner(age, name):
    if age > 18:
        return f'{name} is an adult.'
    else: 
        return f'{name} is a child.'

def outer(name, age):
    info = inner(name, age)
    return f'This is {name}. {info}'


# tests.py

from functions import inner, outer
from unittest.mock import Mock, patch

def test_inner():
    name, age = "John", 43
    info = inner(age, name)
    expected = "John is an adult."
    assert info == expected

def test_outer():
    name, age = "John", 43

    mock_get_info = Mock()
    mock_get_info.return_value = "Info"
    patch_get_info = patch("functions.inner", new=mock_get_info)

    with patch_get_info:
        info = outer(name, age)
        expected = 'This is John. Info'
        assert info == expected
        mock_get_info.assert_called_once_with(name, age)

功能:

  • 两个函数及其相应的测试。
  • inner 函数生成一个字符串,由 test_inner 函数检查该字符串的正确性。
  • outer 函数调用 inner 函数并将其连接到它自己的字符串。 test_outer 函数检查正确的串联和 inner 函数调用。
  • 此外,除了这个最小的示例之外,inner 函数可能会生成一个非常大的字符串,我不想在 test_outer 中显式检查该字符串,这就是为什么内部返回值被模拟。

您可能已经注意到,outer 函数实际上以错误的方式将参数传递给 inner 函数。这是因为我本可以决定更改 inner 函数的参数顺序并相应地更改 test_inner,但忘记了 outer 函数调用 inner 函数。这不会被 test_outer 捕获,因为它是内部一致的。我们只在生产中发现内部函数抛出错误。

如何确保下游函数的所有测试都能捕获修改后的函数定义?

最佳答案

我认为你走在正确的道路上。我相信您的测试没有捕获它,因为您断言了错误的顺序。

mock_get_info.assert_used_once_with(name,age) 应该是: mock_get_info.assert_used_once_with(age, name) 来匹配 inner(age, name) 签名。

尽管如此,我认为更可靠的方法是使用关键字参数,然后断言调用参数是预期的字典。

例如:

# functions.py

def outer(name, age):
    info = inner(age=age, name=name)
    return f'This is {name}. {info}'

# tests
def test_outer():
    name, age = "John", 43

    mock_get_info = Mock()
    mock_get_info.return_value = "Info"
    patch_get_info = patch("functions.inner", new=mock_get_info)

    with patch_get_info:
        info = outer(name, age)
        expected = 'This is John. Info'
        assert info == expected
        _, actual_kwargs = mock_get_info.call_args
        assert actual_kwargs == {'name': name, 'age': age}

如果您想更严格,可以使用 * [1] 强制 inner 仅接受关键字参数。但这也意味着您必须提供默认值,这根据您的用例可能没有意义。

示例:

def inner(*, age=0, name='default'):
    if age > 18:
        return f'{name} is an adult.'
    else:
        return f'{name} is a child.'

如果有人在不使用关键字参数的情况下调用它,Python 将在运行时引发异常。

Traceback (most recent call last):
  File "/home/user/projects/so-test/functions.py", line 14, in <module>
    outer('user', 32)
  File "/home/user/projects/so-test/functions.py", line 9, in outer
    info = inner(age, name)
TypeError: inner() takes 0 positional arguments but 2 were given

在我看来,inner(age=name, name=age)inner(age, name) 更难犯这样的错误。

[1] https://www.python.org/dev/peps/pep-3102/

关于Python测试: How to test correct function calls of downstream functions?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69130033/

相关文章:

c# - MVC SelectListItem 不实现等于吗?

java - 是否有针对 maven-javadoc-plugin 错误 MJAVADOC-414 的解决方法?

python - 构建 JSON 编码的字典 - python

python - 从字典中分配和调用内置函数 (Python)

python - 如何为 GitPython 克隆/pull 函数编写单元测试?

javascript - 如何编写一个期望在 Jasmine 中抛出 'Error' 的测试?

android - Gradle 测试能否与旧式测试项目和平共处

python - 为什么 Numba 不改进这次迭代......?

python - 如何使用正则表达式选择特定扩展名的文件路径

angular - 使用 tick 和 fakeAsync 对 Observable 进行单元测试 Angular 组件