如何在Python中为classmethod编写存根

时间:2009-11-05 18:15:35

标签: python unit-testing

我有一个方法,它需要另一个类的类方法

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):
    ...

我要为 get_interface_params_by_mac 方法编写单元测试,我想在其中更改 remote_command 的实现(我认为它调用存根 - 修复我,如果我错了)

在Python中使用正确的方法是什么?

1 个答案:

答案 0 :(得分:7)

您的单元测试代码(可能在其setUp方法中,如果需要跨多个测试方法,因此有资格作为夹具)应该这样做:

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)

然后撤消此猴子修补(例如,如果在tearDown中完成修补,则使用setUp方法)

somemodule.RemoteCommand.remote_command = self.save_remote_command 

在测试之后并不总是需要把东西放回去,但这是一般的做法。

更优雅的方法是通过依赖注入(DI)模式设计可测试性代码:

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以极低的成本为您带来了很大的灵活性(可测试性,但也在许多其他环境中),这使它成为我最喜欢的设计模式之一(我更愿意尽可能避免猴子修补) 。当然,如果你设计你的测试代码来使用DI,你需要在测试中做的就是通过调用实例的set_remote_command_function和你想要使用的假函数来准备该实例!