I am using pytest3.7 to test. I would like to mock res
,which is return value from get_res_function
. res.property1[key1][keyN].property2
is the value I want to mock. Here is my test:
@mock.patch("mypkg.get_res_function")
def test_function(mock_res):
returner = mock.MagicMock()
returner.property1["key1"]["key2"].property2 = "11111"
returner.property1["key1"]["key3"].property2 = "22222"
mock_res.return_value = returner
However, both values i want to mock only use the first line mock's value, which means mock_res.property1["key1"]["key2"].property2 = "11111", mock_res_property1["key1"]["key3"].property2 = "11111"
and if reverse it in test code, meaning put "22222" before "11111",
returner.property1["key1"]["key2"].property2 = "22222"
returner.property1["key1"]["key3"].property2 = "11111"
then all the results is "22222", what is wrong?
This is tricky because the item access syntax thing[index]
is actually calling the magic method __getitem__
, and that's what you want to mock.
So this
returner.property1["key1"]["key2"].property2 = "11111"
Is like doing this
returner.property1.__getitem__.return_value.__getitem__.return_value.property2 = "11111"
Which is ok, since it's a bit more concise, however the keys are actually ignored.
You can use a PropertyMock
with side_effect
to return different things in order:
type(returner.property1.__getitem__.return_value.__getitem__.return_value).property2 = PropertyMock(side_effect=["11111", "22222"])
Or, back to get item syntax:
type(returner.property1["this is"]["ignored"]).property2 = PropertyMock(side_effect=["11111", "22222"])
And it should work:
returner.property1["foo"]["bar"].property2
> '11111'
returner.property1["foo"]["bar"].property2
> '22222'
However, keep in mind that it will return the same values regardless of the input keys. And it will raise StopIteration
if it runs out of side_effects
.
You can assert which keys are actually used to call __getitem__
:
returner.property1.__getitem__.call_args_list
returner.property1.__getitem__.return_value.__getitem__.call_args_list
If you prefer, you could pass a function as side_effect
instead, it would be called with the same arguments as the mock (the key) and then you could decide what to return depending on the key. But this would need to be done in the __getitem__
mock, not the property mock.
Some references: