【问题标题】:Is it possible to patch a class instance with an existing Singleton instance?是否可以用现有的 Singleton 实例修补类实例?
【发布时间】:2015-03-25 12:48:53
【问题描述】:


我有一个代表我们的 DB 层的类,它在某些类内部被实例化(我不能将它作为外部参数传递)
例如:

class MyClass(object):
    def __init__(self):
        self.dbapi = DatabaseAPI()
        self.timeout = 120

    def some_methods(self):
        pass

我们正在编写一些单元测试,并且我们想使用我们将在测试运行之前创建的现有实例来模拟 self.dbapi。

例如:

my_dbapi = DatabaseAPIMock()
...
...
@patch('MyModule.DatabaseAPI', my_dbapi)
def my_test(self):
    my_class = MyClass() #<---This is where I'm not able to mock the DatabaseAPI

到目前为止,这是我试图实现的目标,但通过调试代码,我看到 self.dbapi 是用真实对象实例化的,而不是用预制的模拟实例化的。

我错过了什么?
顺便说一句,我们正在运行 python 2.7

提前致谢!

【问题讨论】:

  • 您能解释一下如何将DatabaseAPI 导入MyModule 吗?
  • 我已经完全改变了我的答案。

标签: python unit-testing nose nosetests python-unittest


【解决方案1】:

你打错了补丁。您需要修补分配属性的模块,即包含目标类的模块。

如果你在你的目标类中定义一个方法来获取你的 DatabaseAPI 对象会更容易。这样你就可以更容易地修补它。

class MyClass(object):
    def __init__(self):
        self.dbapi = self.get_db_api()
        self.timeout = 120

    def get_db_api():
        return DatabaseAPI()

然后测试变成:

@patch('my_module.MyClass.get_db_api')
def my_test(self, my_method):
    my_method.return_value = my_dbapi

【讨论】:

  • 嗨丹尼尔,感谢您的快速响应,我“跳过”了那部分是我的错,但在我的代码中,我实际上修补了完整的类路径,包括模块。我会更新问题
  • 关于您的第二个建议,我们有数百个类以与 MyClass 类似的方式编写。我正在尝试创建一个适合所有人的补丁装饰器,而无需为每个测试创建不同的补丁(随着类名的变化)。另外,除非没有其他办法,否则我宁愿不更改这些类的代码。
【解决方案2】:

简答:使用

@patch('MyModule.DatabaseAPI', return_value=my_dbapi)

改为。

MyModuleDatabaseAPI 类似于工厂,但my_dbapi 是一个实例。因此,通过将实例设置为工厂的返回值,您可以模拟 self.dbapi 引用。

如果在生产代码中您可以稍微更改设计,请考虑按照@DanielRoseman 的回答建议移动:这是一个更好的设计。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-25
    • 1970-01-01
    • 2022-08-10
    • 2010-09-19
    • 1970-01-01
    相关资源
    最近更新 更多