Python单元测试中的依赖注入

Python单元测试中的依赖注入,python,mocking,python-unittest,Python,Mocking,Python Unittest,我正在学习python 我想知道是否有一种机制可以“注入”一个对象(在我的例子中是一个伪对象)到被测试的类中,而不显式地将其添加到costructor/setter中 ## source file class MyBusinessClass(): def __init__(self): self.__engine = RepperEngine() def doSomething(self): ## bla bla ... succ

我正在学习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…)但没有成功

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

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

Python中不需要IOC。这里有一个蟒蛇式的方法

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(.....)
您还可以将该类去掉以绕过构造函数

class MyBusinessClassFake(MyBusinessClass):
   def __init__(self):  # we bypass super's init here
      self._engine = None
然后在你的设置中

def setUp(self):
  self.helper = MyBusinessClassFake()
现在在测试中,您可以使用上下文管理器

def test_XXXXX_whenYYYYYY(self):
  with mock.patch.object(self.helper, '_engine', autospec=True) as mock_eng:
     ...
如果您想使用构造函数在不使用out的情况下注入它,那么可以将其添加为类属性

class MyBusinessClass():
    _engine = None
    def __init__(self):
        self._engine = RepperEngine() 
现在要绕过
\uuuu init\uuuu

class MyBusinessClassFake(MyBusinessClass):
   def __init__(self):
      pass
现在,您可以简单地分配值:

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

在多年使用Python而不使用任何DI自动连接框架和Java with Spring之后,我逐渐意识到简单的Python代码通常不需要框架来进行依赖注入而不使用自动连接(自动连接是Guice和Spring在Java中都做的),也就是说,只做这样的事情就足够了:

def-foo(dep=None):#非常适合单元测试!
self.dep=dep或dep()#呼叫者也不能关心这一点
...
这是纯粹的依赖注入(非常简单),但没有神奇的框架为您自动注入它们(即自动连线),也没有控制反转

与@Dan不同,我不同意Python不需要IoC:控制反转是一个简单的概念,框架剥夺了对某些东西的控制,通常是为了提供抽象和删除样板代码。当您使用模板类时,这就是IoC。如果IoC是好是坏,完全取决于框架如何实现它

尽管如此,依赖注入是一个简单的概念,不需要IoC。自动布线DI

当我处理更大的应用程序时,这种简单化的方法不再能够解决这个问题:有太多的样板代码,而DI的主要优点却缺失了:一次更改某个东西的实现,并将其反映在依赖它的所有类中。如果应用程序的许多部分关心如何初始化某个依赖项,而您更改了该初始化或想要更改类,那么您必须逐个更改它。使用DI框架会更容易

因此,我提出了一个微型框架,它不会让人觉得不符合python,但可以提供一流的依赖注入自动连接

在人类的座右铭下™ 这就是它看起来的样子:

#some_service.py
服务类别:
@自动连线
定义初始化__(
自己
数据库:自动连线(数据库),
消息代理:自动连线(列表[代理]),
):
挂起=数据库。检索挂起的消息()
对于message_brokers中的broker:
broker.send_挂起(挂起)
#database.py
@注射的
类数据库:
...
#message_broker.py
类别MessageBroker(ABC):
def发送_挂起(消息):
...
#kafka#u producer.py
@注射的
KafkaProducer类(MessageBroker):
...
#sqs_producer.py
@注射的
SQSProducer类(MessageBroker):
...

“我想“注入”对象“引擎”,而不将其传递给构造函数”。我没有得到,什么不清楚?其他语言(如java)中的其他单元测试框架可以在没有setter或costructor的情况下将对象注入其他对象中。这还不清楚,因为您同时要求以一种方式注入“MyBusinessClassFake”和“engine”,这种方式不涉及使用monkey修补、继承或传递任何值,而且必须是自动的。如果@patch是不可能的,那么您确定不能只执行以下操作吗?unit=MyBusinessClass()unit.\uu engine=RepperEngine()抱歉,有误会。我在哪里说过我不想使用@patch?补丁对我很好。。。我刚才说我无法让它与@patch一起工作……这是错误的。它带来了很大的麻烦,考虑到有人添加了一个新的从实际数据库中创建或删除的可选参数,那么所有的UNITTEST都可以直接访问它,而不显式禁用,这使得UNITST成为危险。传入显式或使用di框架,只是不要默认为prod服务。