[TIP] [python] Mocking and patching a class with mock.py?
Michael Foord
fuzzyman at voidspace.org.uk
Sun Jan 20 14:58:54 PST 2008
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