derwiki-adroll / mock

Automatically exported from code.google.com/p/mock
BSD 2-Clause "Simplified" License
0 stars 0 forks source link

assert_called_with does not capture content of dicts at the time of call under test #184

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?

Try running this test sample:

import unittest
from mock import Mock

class TestBug(unittest.TestCase):
    def test_weird_bug(self):
        mock = Mock()
        my_dict = {'foo': 1}
        mock.do_something(my_dict)
        my_dict.update({'bar': 2})
        mock.do_something.assert_called_with({'foo': 1}) # should pass

What is the expected output? What do you see instead?

This test should pass but fails. What seems to be happening is that Mock simply 
treats the dict like an object pointer and doesn't capture/evaluate it's 
contents until assert_called_with is invoked. If assert_called_with is able to 
compare the expected and actual dict args based on their contents then really 
the values should be captured at the point the method call under test is made. 

What version of the product are you using? On what operating system?

1.0b1

Please provide any additional information below.

Original issue reported on code.google.com by t...@leach.it on 12 Oct 2012 at 11:03

GoogleCodeExporter commented 9 years ago
This is a known issue of using mock objects with mutable arguments. Copying 
arguments by default would break "equality by identity" asserts.

See this discussion, including two solutions, in the documentation:

     http://www.voidspace.org.uk/python/mock/examples.html#coping-with-mutable-arguments

Original comment by fuzzyman on 15 Oct 2012 at 11:23

GoogleCodeExporter commented 9 years ago
I understand you don't want to break equality by identity. 

Having mulled this over a little more and compared mock's approach with other 
mocking frameworks I've used in the past, I think the essential root problem 
here is that method call expectations are expressed *after* the method under 
test is called. 

e.g. in RSpec-mock in Ruby you would do something like:

double.should_receive(:do_something).with({:key => 'value'})
double.do_something({:key => 'value'})  # passes

The advantage with this approach is that the expectation is evaluated at the 
point of the method call and not some time later, eliminating the possibility 
that the arguments being matched can be mutated in the interim. 

To be clear, I'm not suggesting you implement this pattern in mock :-) just 
wanted to brain dump my thoughts.

Original comment by t...@leach.it on 15 Oct 2012 at 1:48

GoogleCodeExporter commented 9 years ago
Sure, that's the record->replay pattern. mock is instead based on AAA 
(Arrange->Act->Assert).

Original comment by fuzzyman on 15 Oct 2012 at 1:50