嵌套多个依赖项时如何模拟Python类

时间:2018-01-03 20:47:07

标签: python python-2.7 unit-testing testing mocking

如果我有以下架构......

请注意以下修改内容。我(在最近的一些重构之后)发现在三个不同的文件中实际上有三个类。对不起,文件/类名变得荒谬了。我向你保证那些不是真名。 :)

main_class.py

class MainClass(object):
    def do_some_stuff(self):
        dependent_class = DependentClass()



dependent_class.py

class DependentClass(object):
    def __init__():
        dependent_dependent_class = DependentDependentClass()
        dependent_dependent_class.do_dependent_stuff()



dependent_dependent_class.py

class DependentDependentClass(object):
    def do_dependent_stuff(self):
        print "I'm gonna do production stuff that I want to mock"
        print "Like access a database or interact with a remote server"

class MockDependentDependentClass(object):
    def do_dependent_stuff(self):
        print "Respond as if the production stuff was all successful."

我希望在测试期间调用main_class.do_some_stuff,但在执行期间,我希望DependentDependentClass的实例替换为MockDependentDependentClass如何使用最佳实践进行pythonically。

目前,我能想到的最好的事情是根据环境变量的存在/值有条件地实例化一个类或另一个类。它确实有效但很脏。

我花了一些时间阅读有关unittest.mock和mock.patch函数的内容,看起来他们似乎能够提供帮助,但我可以包围的每个描述似乎与我的实际用例有点不同。

关键是我不想定义模拟返回值或属性,但我希望命名空间更改,全局,我想,当我的应用程序认为它实例化DependentClass时,实际上是实例化MockDependentClass。

事实上,我无法找到任何正在做这件事的人的例子,这意味着以下两点之一:

  1. 这是因为我以一种非常愚蠢/天真的方式做这件事。
  2. 我正在做一些天才,没有其他人遇到过它。
  3. ......我认为它是第一...

    完全公开,单元测试不是我熟练的。我的内部工具开发团队正在努力赶上我们的游戏,这是一项努力。我可能没有考虑正确测试。

    任何想法都会受到欢迎。提前谢谢!

    解!!!

    感谢@ de1的帮助。鉴于我上面显示的聪明的架构,以下内容实现了我想要的目标。

    以下代码位于main_class.py

    import dependent_class
    from dependent_dependent_class import MockDependentDependentClass
    
    with patch.object(dependent_class, "DependentDependentClass", MockDependentDependentClass):
        main_class = MainClass()
        main_class.do_some_stuff()
    

    代码似乎(如果我知道它是如何做的话,地狱)操纵模块dependent_class中的命名空间,以便在with块内(' sa挂在那个部分上的任何人的上下文管理器)引用类对象DependentDependentClass的任何内容实际上都会引用MockDependentDependentClass

1 个答案:

答案 0 :(得分:1)

mock module确实在这种情况下确实很合适。您可以指定在调用各种patch方法时使用的mock(您的模拟)。

如果您只导入类而不是模块,则可以在DependentClass中修补导入的DependentDependentClass:

import .DependentClass as dependent_class
from .DependentDependentClass import MockDependentDependentClass

with patch.object(dependent_class, 'DependentDependentClass', MockDependentDependentClass):
  # do something while class is patched

可替换地:

with patch('yourmodule.DependentClass.DependentDependentClass', MockDependentDependentClass):
  # do something while class is patched

或以下内容仅在您通过模块访问课程或在修补后导入课程时才有效:

with patch('yourmodule.DependentDependentClass.DependentDependentClass', MockDependentDependentClass):
  # do something while class is patched

请记住修补的对象是什么时候。

注意:您可能会发现在较小的情况下命名文件时不那么容易混淆,与嵌入式类略有不同。

注意2:如果你需要模拟被测模块的依赖关系,那么它可能暗示你没有在正确的级别进行测试。