0

I have a method which calls for a classmethod of another class

def get_interface_params_by_mac(self, host, mac_unified):
        lines = RemoteCommand.remote_command(host, cls.IFCONFIG)

...

class RemoteCommand(object):

    @classmethod
    def remote_command(cls, host, cmd, sh = None):
    ...

I'm going to write a unit test for get_interface_params_by_mac method, in which I'd like to change an implementation of remote_command (I think it calls stub - fix me if I wrong)

What the right way to do this in Python?

1 Answer 1

7

Your unit-test code (maybe in its setUp method, if this is needed across several test methods and thus qualifies as a fixture) should do:

def fake_command(cls, host, cmd, sh=None):
  pass  # whatever you want in here
self.save_remote_command = somemodule.RemoteCommand.remote_command
somemodule.RemoteCommand.remote_command = classmethod(fake_command)

and then undo this monkey-patching (e.g. in the tearDown method if the patching is done in setUp) by

somemodule.RemoteCommand.remote_command = self.save_remote_command 

It's not always necessary to put things back after a test, but it's good general practice.

A more elegant approach would be to design your code for testability via the Dependency Injection (DI) pattern:

def __init__(self, ...):
   ...
   self.remote_command = RemoteCommand.remote_command
   ...

def set_remote_command_function(self, thefunction):
   self.remote_command = thefunction

def get_interface_params_by_mac(self, host, mac_unified):
        lines = self.remote_command(host, cls.IFCONFIG)

DI buys you a lot of flexibility (testability-wise, but also in many other contexts) at very little cost, which makes it one of my favorite design patterns (I'd much rather avoid monkey patching wherever I possibly can). Of course, if you design your code under test to use DI, all you need to do in your test is appropriately prepare that instance by calling the instance's set_remote_command_function with whatever fake-function you want to use!

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, Alex. This is exactly what I need. I'd like also to read something about DI. Could you give me a link?
Another question... Do I really need a method set_remote_command_function? As I understand I can write something like this: myobj.remote_command = RemoteCommand.remote_command
@legesh, for DI, see e.g the PDF of a presentation of mine at aleax.it/yt_pydi.pdf . No, you don't necessarily need a setter-method, direct attribute assignment (possibly through a property if you need to do some book-keeping upon such assignments) can work too.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.