This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Unexpected record of call_args_list when passing mutable object to mock.patch
Type: behavior Stage: resolved
Components: Tests Versions: Python 3.11, Python 3.10, Python 3.9
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: andrei.avk, dmcnulla, kj
Priority: normal Keywords:

Created on 2021-09-27 21:28 by dmcnulla, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (4)
msg402743 - (view) Author: Dave McNulla (dmcnulla) Date: 2021-09-27 21:28
https://gist.github.com/dmcnulla/ecec8fc96a2fd07082f240eeff6888d9
I'm trying to reproduce an error in a call to a method, forcing a second call to the method. In my test, the call_args_list is showing incorrectly (both in debugging or running unittest normally).

I am not certain what other circumstances this is happening or not happening. I was able to reduce the code to reproduce it quite a bit. I am certain that the first call to the method is not matching correctly in my criteria to what I expect based on my debugging.

I am using python 3.7. I can reproduce in IntelliJ or by commandline `python3 -m pytest test_multiple_side_effect.py`
msg404828 - (view) Author: Andrei Kulakov (andrei.avk) * (Python triager) Date: 2021-10-22 21:22
Dave: what seems to happen here is that mock correctly reporting to you that the object you passed to the mocked func is currently `{}`. What you probably expected it to be is to be equal to what it was at exact time when it was passed, i.e. before it was modified inside the method.

That's a reasonable expectation but it means it would have to be deepcopied, which could be expensive for large structures.

Do you think that noting this nuance in the docs for call_list_args would have helped you debug it in this case?
msg404840 - (view) Author: Dave McNulla (dmcnulla) Date: 2021-10-22 23:26
I understand. It's a thing I often forget in python that some parameters are passed by value while others are passed by reference (language for parameters I remember from C class about 30 years ago).

I do not think I would have caught that with docs, unless you start putting them in Stack Overflow! I'm only sort of kidding.

I think you can close this bug.

Thanks for the clarification,

Dave
msg404847 - (view) Author: Andrei Kulakov (andrei.avk) * (Python triager) Date: 2021-10-23 00:22
This issue also came up with MagicMock on stackoverflow: https://stackoverflow.com/questions/23257227/python-mock-method-call-arguments-display-the-last-state-of-a-list/23264042

Dave: glad this helped to clear it up. For the record, the arguments in Python are passed by object reference, which is not the same as by reference or by value; - it means that mutable objects can be mutated by receiving func and immutable objs cannot be mutated. In your case it was a mutable object, you mutated it, but compared it to value that it had before mutation.

I've thought if this should be cleared up in the docs, but I think in most cases it will be fairly obvious to see what happened because the tested value will differ from expected exactly in the way that the function modifies it. But if more people run into this confusion, we should consider adding a note to MagicMock, call_args, and call_args_list (and maybe some other mock attrs).
History
Date User Action Args
2022-04-11 14:59:50adminsetgithub: 89468
2021-10-23 00:22:46andrei.avksetstatus: open -> closed
versions: + Python 3.9, Python 3.10, Python 3.11, - Python 3.7
title: Incorrect record of call_args_list when using multiple side_effect in mock.patch -> Unexpected record of call_args_list when passing mutable object to mock.patch
messages: + msg404847

type: behavior
stage: resolved
2021-10-22 23:26:24dmcnullasetresolution: not a bug
messages: + msg404840
2021-10-22 21:22:58andrei.avksetnosy: + kj
2021-10-22 21:22:46andrei.avksetnosy: + andrei.avk
messages: + msg404828
2021-09-27 21:28:08dmcnullacreate