Search code examples
pythonunit-testingpython-mock

How do I patch a method of a property object in python


I have a package that looks like this:

lcl
 |
 | - lcl
      | - LCL.py
      | - lunabuild.py

In the lunabuild module, there is a Lunabuild class. The Lunabuild class has a method Lunabuild.configure_jobs(). The LCL class imports lunabuild:

from lcl import lunabuild

It then uses it as a lazily instantiated property:

@property
def luna_build(self):
  self._luna_build = lunabuild.Lunabuild()

the LCL class has a main() method that runs the Lunabuild.configure_jobs() method:

main(self)
  try:
    self.luna_build.configure_jobs()
  except ValidationError:
     ...
  except Exception, e:
     return_code = 2
     self.logger_exception(e)

I'm trying to patch configure_jobs to have a side_effect that raises an error, so that I can test that unknown errors are logged as expected.

I have been unable to figure out where to patch. it keeps telling me that it doesn't recognize the attribute, or that lunabuild has no configure_jobs attribute.

I've tried a bunch of different options. Running with a debugger attached, I can see that test_lcl in the following code is an lcl.lunabuild.Lunabuild object

UPDATE: my latest attempt is

with patch.object('lcl.lunabuild.Lunabuild', 'configure_jobs') as mock:
  mock.side_effect = OSError(2, 'message')
  retcode = test_lcl.main()  
  self.assertEquals(retcode, 2)

Solution

  • The simpler way to do it is to path the static reference of configure_jobs method in Lunabuild class definition. So use follow code should do exactly what you need

    with patch('lcl.lunabuild.Lunabuild.configure_jobs', side_effect=OSError(2, 'message')) as mock:
        retcode = test_lcl.main()  
        self.assertEquals(retcode, 2)
    

    If you want patch just the object that you use in your test you can do it by:

    with patch.object(test_lcl.luna_build, 'configure_jobs', side_effect = OSError(2, 'message')) as mock:
        retcode = test_lcl.main()  
        self.assertEquals(retcode, 2)
    

    My taste is to use patch.object just when I have no other chance:

    1. It is more complicated to understand what you are doing
    2. To use it you should know more about objects to patch the right things