[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