1

I have a function similar to foo and I am trying to test it using following code

from unittest.mock import call, patch

def foo():
    x = {}
    for i in range(3):
        x["val"] = i
        print(x)

@patch('builtins.print')
def test_foo(print_mock):
    foo()
    calls = calls = [call({'val': 0}), call({'val': 1}), call({'val': 2})]
    print_mock.assert_has_calls(calls)

test_foo()

But it gives the following error

File "/usr/lib/python3.8/unittest/mock.py", line 950, in assert_has_calls
    raise AssertionError(
AssertionError: Calls not found.
Expected: [call({'val': 0}), call({'val': 1}), call({'val': 2})]
Actual: [call({'val': 2}), call({'val': 2}), call({'val': 2})]

I think the issue is that it's taking the last value with which the function was called. Would appreciate a fix or some alternate way to test the foo function

Update

python documentation has some workaround for this issue

Akshat Karani
  • 73
  • 1
  • 6
  • This behaves as expected - the function is called 3 times with `x`, which has the value as shown during the test. Maybe you actually wanted to print `i` or `x["val"]`? – MrBean Bremen Oct 01 '21 at 14:00
  • @MrBeanBremen The function is called three time with args `{val: 0}`, `{val: 1}`, `{val: 2}`. But the in error it expects all calls as `{val: 2}` – Akshat Karani Oct 04 '21 at 03:41
  • Yes, that is the value of `x` after the call. The mock remembers that it was called with `x` 3 times, it does not make a copy of the argument. – MrBean Bremen Oct 04 '21 at 03:54
  • Okay, so is there no way to test such functions? In `foo` if I update `print(x)` to `print(deepcopy(x))` the test passes. – Akshat Karani Oct 04 '21 at 04:46
  • Yes, exactly. To do what you want, you could use a mock for `print` where you save the call arguments yourself. – MrBean Bremen Oct 04 '21 at 06:26

1 Answers1

0

I think you achieved this behavior?

  1 from unittest.mock import call, patch
  2 
  3 def foo():
  4     x = {}
  5     for key, value in zip(range(3), range(3)):
  6         x[key] = value
  7         print(x)
  8 
  9 @patch("builtins.print")
 10 def test_foo(print_mock):
 11     foo()
 12     calls = [call({0: 0, 1: 1, 2: 2})]
 13     print_mock.assert_has_calls(calls)
 14 
 15 test_foo()

larick
  • 98
  • 1
  • 7