使用Python进行单元测试中的依赖注入

时间:2017-02-22 16:33:41

标签: python mocking python-unittest

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

3 个答案:

答案 0 :(得分:5)

Python中不需要IOC。这是一个Pythonic方法。

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:
     ...

如果你想使用constuctor注入它,那么你可以将它作为类属性添加。

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)

答案 1 :(得分:0)

@ Dan的回答非常简单,只是为了加入讨论:

我是装饰器的忠实粉丝,可以从函数中删除样板代码。范围。

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

说,我维护一个模块来处理:Injectable,它为Python 3提供了一个@autowired装饰器,以实现简单,干净的依赖注入:

  • 该功能无需了解所有
  • 的自动装配
  • 依赖关系可以延迟初始化
  • 如果需要,调用者可以显式传递依赖项实例

装饰者的重点是像这样转动

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

进入

@autowired
def __init__(self, *, model: Model, service: Service):
    self.model = model
    self.service = service
    # actual code

没有复杂的东西,没有设置,没有强制执行工作流程。现在,您的功能代码不再与依赖关系初始化代码混杂在一起。

装饰者的方法很简约。可能是一个完整的框架更适合你的情况。为此,有一些优秀的模块,如Injector

答案 2 :(得分:-1)

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

class MyBusinessClassFake(MyBusinessClass):
   def __init__(self):
      pass
相关问题