简体   繁体   English

Python Unittest Mock assert_has_calls 返回对其他模拟的调用

[英]Python Unittest Mock assert_has_calls returning calls to other mocks

Here's a toy example that illustrates my problem.这是一个说明我的问题的玩具示例。

Code代码

class Bar:
    def do_a_thing(self):
        print('doing a thing')


class BarSupplier:
    def get_bar(self) -> Bar:
        return Bar()


class Foo:
    def __init__(self, bar_supplier: BarSupplier):
        self.bar_supplier = bar_supplier

    def do_foo(self):
        self.bar_supplier.get_bar().do_a_thing()

Tests测试

from unittest import TestCase
from unittest.mock import MagicMock, call

from calls_example import Foo


class TestCallsExample(TestCase):
    def test_once(self):
        bar_supplier = MagicMock()
        bar_supplier.get_bar.return_value = MagicMock()

        foo = Foo(bar_supplier)

        foo.do_foo()

        bar_supplier.get_bar.assert_has_calls([
            call(),
        ])

    def test_twice(self):
        bar_supplier = MagicMock()
        bar_supplier.get_bar.return_value = MagicMock()

        foo = Foo(bar_supplier)

        foo.do_foo()
        foo.do_foo()

        bar_supplier.get_bar.assert_has_calls([
            call(),
            call()
        ])

Results结果

The first test passes.第一个测试通过。

The second test fails, with the following exception:第二个测试失败,但出现以下异常:

Failure
Traceback (most recent call last):
  ...
AssertionError: Calls not found.
Expected: [call(), call()]
Actual: [call(), call().do_a_thing(), call(), call().do_a_thing()]

This feels like really strange behaviour - I'm asserting about calls to the get_bar method on the bar_supplier mock, but then the calls list includes calls to a different mock which is returned by the get_bar method.这感觉真的很奇怪——我断言调用bar_supplier mock 上的get_bar方法,但随后调用列表包括对get_bar方法返回的不同 mock 的调用。

I'm sure this is a misunderstanding rather than a bug, but how can I best avoid getting those .do_a_thing() calls in my list of calls?我确信这是一种误解而不是错误,但是我怎样才能最好地避免在我的调用列表中收到那些.do_a_thing()调用?

It is because the same mock for .get_bar() always subsequently calls .do_a_thing() which as documented :这是因为.get_bar()的相同模拟总是随后调用.do_a_thing() ,如记录的那样:

assert_has_calls(calls, any_order=False)

assert the mock has been called with the specified calls.断言已使用指定的调用调用模拟。 The mock_calls list is checked for the calls.检查mock_calls列表中的调用。

Wherein mock_calls includes not just the calls to itself:其中mock_calls 不仅包括对自身的调用:

mock_calls

mock_calls records all calls to the mock object , its methods , magic methods and return value mocks . mock_calls 记录了对 mock object及其方法、魔术方法和返回值 mocks的所有调用。

Solution 1解决方案 1

You can use the any_order=True setting for assert_has_calls which as documented:您可以对assert_has_calls使用any_order=True设置,如文档所述:

If any_order is false then the calls must be sequential .如果any_order为 false 则调用必须是顺序的。 There can be extra calls before or after the specified calls.在指定的调用之前或之后可以有额外的调用。

If any_order is true then the calls can be in any order , but they must all appear in mock_calls.如果any_order为真,则调用可以按任何顺序进行,但它们必须全部出现在 mock_calls 中。

So change:所以改变:

bar_supplier.get_bar.assert_has_calls([
    call(),
    call()
])

To:到:

bar_supplier.get_bar.assert_has_calls([
    call(),
    call()
],
any_order=True)

Solution 2方案二

An alternative is checking call_args_list instead:另一种方法是检查call_args_list

assert bar_supplier.get_bar.call_args_list == [
    call(),
    call()
]

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

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