[TIP] [python] Mocking and patching a class with mock.py?
Daryl Spitzer
daryl.spitzer at gmail.com
Sun Jan 20 21:46:44 PST 2008
Thanks Michael. I get it now. I went with just setting the mock's
return value to itself. It might be a little more confusing to the
uninitiated, but I think one can get used to that idiom quickly. (And
I don't feel the need to use two mocks in this case.)
So the working code looks like:
-----
mock = Mock(spec=doessomething.MyClass)
mock.return_value = mock # so MyClass() returns a mock instance
@patch('doessomething', 'MyClass', mock)
def test_does_something(self):
doessomething.does_something()
self.assertEqual(TestDoessomething.mock.do_something.call_args_list,
[(('x',), {})])
-----
Though I find myself using this pattern instead:
-----
def test_does_something(self):
mock = Mock(spec=doessomething.MyClass)
mock.return_value = mock
@apply
@patch('doessomething', 'MyClass', mock)
def do_test():
doessomething.does_something()
self.assertEqual(mock.do_something.call_args_list,
[(('x',), {})])
-----
As the number of tests grows, it seems safer to define the mocks
inside the test methods, even if I do then have to have an internal
function in order to use the patch decorator.
I'd love to read anyone's suggestions for improvement though.
--
Daryl
On Jan 20, 2008 2:58 PM, Michael Foord <fuzzyman at voidspace.org.uk> wrote:
>
> Daryl Spitzer wrote:
> > I just started using Michael's mock.py (version 0.3.1) and I'm pleased
> > that I've been able to remove many lines from my test code and make it
> > more readable.
> >
> > But I'm stuck trying to patch a class with a mock. I think some
> > sample code would better explain my problem:
> >
> > doessomething.py:
> > -----
> > class MyClass(object):
> > def do_something(self, arg):
> > print 'arg:', arg
> >
> > def does_something():
> > obj = MyClass()
> > obj.do_something('x')
> >
> > if __name__ == "__main__":
> > does_something()
> > -----
> >
> > doessomething_test.py
> > -----
> > import doessomething
> >
> > import unittest
> > from mock import Mock, patch
> >
> > class TestDoessomething(unittest.TestCase):
> > mock = Mock(spec=doessomething.MyClass)
> > @patch('doessomething', 'MyClass', mock)
> > def test_does_something(self):
> > doessomething.does_something()
> > self.assertEqual(mock.do_something.call_args_list,
> > [(('x',), {})])
> >
> > if __name__ == "__main__":
> > unittest.main()
> > -----
> >
> > When I run `python doessomething.py`, I get "arg: x" as expected.
> >
> > When I run `python doessomething_test.py`, I get "AttributeError:
> > 'NoneType' object has no attribute 'do_something'" on
> > "obj.do_something('x')" in doessomething.py.
> >
> > Does what I'm trying to do (replace MyClass in doessomething.py) with
> > a mock make sense? Can it be done with the Mock.__init__() spec
> > argument?
> >
> > I tried the following version of TestDoessomething (in
> > doessomething_test.py), but get the same error:
> > -----
> > class TestDoessomething(unittest.TestCase):
> > def test_does_something(self):
> > mock = Mock(spec=doessomething.MyClass)
> > try:
> > old_my_class = doessomething.MyClass
> > doessomething.MyClass = mock
> >
> > doessomething.does_something()
> > self.assertEqual(mock.do_something.call_args_list,
> > [(('x',), {})])
> > finally:
> > doessomething.MyClass = old_my_class
> > -----
> >
> > So the problem is must be a misunderstanding of (or possibly a bug in)
> > how the spec argument works with classes.
> >
>
> The problem is that you are patching a class with a Mock instance. When
> the class is instantiated in 'does_something' the mock is called (and by
> default returns None).
>
> If you add the following line to your code it should do the right thing:
>
> mock.return_value = mock
>
> or you could create a second mock (perhaps named 'instance') and track
> how that is used.
>
> Michael
> http://www.manning.com/foord
>
>
> > --
> > Daryl Spitzer
> >
> >
>
>
More information about the testing-in-python
mailing list