Python单元测试中的依赖注入

2024-10-01 09:18:59 发布

您现在位置:Python中文网/ 问答频道 /正文

我在学python

我想知道是否有一种机制可以将一个对象(在我的例子中是一个伪对象)“注入”到被测类中,而不显式地将它添加到costructor/setter中。在

## source file
class MyBusinessClass():
    def __init__(self):
        self.__engine = RepperEngine()

    def doSomething(self):
        ## bla bla ...
        success

## test file
## fake I'd like to inkject
class MyBusinessClassFake():
   def __init__(self):
      pass

def myPrint(self) :
    print ("Hello from Mock !!!!")

class Test(unittest.TestCase):

    ## is there an automatic mechanism to inject MyBusinessClassFake 
    ## into MyBusinessClass without costructor/setter?
    def test_XXXXX_whenYYYYYY(self):

        unit = MyBusinessClass()
        unit.doSomething()
        self.assertTrue(.....)

在我的测试中,我想“注入”对象“引擎”,而不把它传递给构造器。我尝试过几个选项(例如:@patch…)但没有成功。在


Tags: to对象testselfinitdefunitclass
3条回答

@Dan的回答非常透彻,但只是为了增加讨论:

我是一个从函数范围中删除样板代码的装饰器的忠实粉丝。在

虽然将依赖项作为另一个参数非常简单,但它添加了无关的代码以确保在大多数上下文中初始化所有依赖项。在

也就是说,我维护了一个模块来处理这个问题:Injectable,它为Python 3提供了一个@autowired修饰符,以实现简单而干净的依赖注入:

  • 该功能完全不必知道自动布线
  • 依赖项可以延迟初始化
  • 如果需要,调用者能够显式地传递依赖关系实例

decorator的全部目的是像这样转换代码

def __init__(self, *, model: Model = None, service: Service = None):
    if model is None:
        model = Model()

    if service is None:
        service = Service()

    self.model = model
    self.service = service
    # actual code

进入这个

^{2}$

没有复杂的东西,没有设置,没有强制执行的工作流。现在你的函数代码不再是依赖初始化代码的混乱了。在

装饰方法非常简约。也许一个成熟的框架更适合你。为此,有一些优秀的模块,如Injector。在

Python中不需要IOC。这是一个Python式的方法。在

class MyBusinessClass(object):
    def __init__(self, engine=None):
        self._engine = engine or RepperEngine() 
        # Note: _engine doesn't exist until constructor is called.

    def doSomething(self):
        ## bla bla ...
        success

class Test(unittest.TestCase):

    def test_XXXXX_whenYYYYYY(self):
        mock_engine = mock.create_autospec(RepperEngine)
        unit = MyBusinessClass(mock_engine)
        unit.doSomething()
        self.assertTrue(.....)

您还可以去掉类来绕过构造函数

^{2}$

然后在你的设置中

def setUp(self):
  self.helper = MyBusinessClassFake()

现在在测试中,您可以使用上下文管理器。在

def test_XXXXX_whenYYYYYY(self):
  with mock.patch.object(self.helper, '_engine', autospec=True) as mock_eng:
     ...

如果您想在不使用构造函数的情况下注入它,那么可以将其作为类属性添加。在

class MyBusinessClass():
    _engine = None
    def __init__(self):
        self._engine = RepperEngine() 

现在要绕过存根__init__

class MyBusinessClassFake(MyBusinessClass):
   def __init__(self):
      pass

现在您可以简单地指定值:

unit = MyBusinessClassFake()
unit._engine = mock.create_autospec(RepperEngine)

您的问题似乎有些不清楚,但是没有什么可以阻止您使用类继承来重写原始方法。在这种情况下,派生类将如下所示:

class MyBusinessClassFake(MyBusinessClass):
   def __init__(self):
      pass

相关问题 更多 >