【问题标题】:Using mocking to test derived classes in Python在 Python 中使用 mocking 测试派生类
【发布时间】:2010-09-29 13:46:55
【问题描述】:

我的代码如下所示:

import xmlrpclib

class Base(object):
    def __init__(self, server):
        self.conn = xmlrpclib.ServerProxy(server)

    def foo(self):
        return self.conn.do_something()

class Derived(Base):
    def foo(self):
        if Base.foo():
            return self.conn.do_something_else()

我应该如何使用模拟来测试Derived 类的行为?我不想假设 XML-RPC 连接所涉及的任何内容都将实际存在,但我觉得模拟 xmlrpclib 模块需要对 Base 类的实现有太多了解(我有其他测试)。

或者,我想,我什至应该使用模拟来测试这个?如果没有,你将如何测试它?

【问题讨论】:

    标签: python mocking


    【解决方案1】:

    您可以创建一个假的 ServerProxy 类,并用它代替测试。

    类似这样的:

    class FakeServerProxy(object):
        def __init__(self, server):
            self.server = server
        def do_something(self):
            pass
        def do_something_else(self):
            pass
    
    def test_derived():
        xmlrpclib.ServerProxy = FakeServerProxy
        derived = Derived(None)
        derived.foo()
    

    【讨论】:

    • 但是我需要知道Base 类使用xmlrpclib。我想尽量避免这种情况,因为如果基类的实现发生更改,我必须更改派生类的测试,而不是派生类的实际实现,这似乎是错误的。
    • Derived 与 Base 紧密耦合,因此您必须在 Base 的内部结构中四处寻找(而不是求助于 voodoo)。让 Base 成为成员而不是祖先,然后伪造 Base 怎么样?
    • 我猜这在 Python 中可以工作,因为鸭子类型,但它不是我的 C++ 方面的一个非常不令人满意的解决方案,因为它不允许替换原则。
    • @Zach Hirsch:所有子类测试也是对超类的测试。您不能将 Derived 视为与 Base 无关。您对 Derived 的测试要么是 Base 测试的子类,要么是复制粘贴的克隆。
    【解决方案2】:

    通过一些微不足道的重构(提取对do_something_else 的调用),您可以测试Derived.foo 逻辑,而无需“了解”XMLRPC。

    import xmlrpclib
    
    class Base(object):
        def __init__(self, server):
            self.conn = xmlrpclib.ServerProxy(server)
    
        def foo(self):
            return self.conn.do_something()
    
    class Derived(Base):
        def foo(self):
            if Base.foo(self):
                return self._bar()
        def _bar(self):
            # moved to its own method so that you
            # you can stub it out to avoid any XMLRPCs
            # actually taking place.
            return self.conn.do_something_else()
    
    import mox
    
    d = Derived('http://deep-thought/unanswered/tagged/life+universe+everything')
    
    m = mox.Mox()
    m.StubOutWithMock(Base, 'foo')
    m.StubOutWithMock(d, '_bar')
    Base.foo(d).AndReturn(True)
    d._bar() # Will be called becase Boo.foo returns True.
    m.ReplayAll()
    
    d.foo()
    
    m.UnsetStubs()
    m.VerifyAll()
    

    或者,甚至最好,您可以将对do_something_else 的调用提取到Base 上的bar 方法中。这是有道理的,如果我们同意 Base 封装了您的所有 XMLRPC 操作。

    该示例使用 pymox 模拟库,但无论您的模拟风格如何,它的主旨都保持不变。

    【讨论】:

      猜你喜欢
      • 2019-08-11
      • 1970-01-01
      • 1970-01-01
      • 2016-12-25
      • 2020-06-08
      • 2021-08-04
      • 2011-01-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多