Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/react-native/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
我应该如何在Python中使用mock进行测试?_Python_Unit Testing_Mocking_Tdd_Python Mock - Fatal编程技术网

我应该如何在Python中使用mock进行测试?

我应该如何在Python中使用mock进行测试?,python,unit-testing,mocking,tdd,python-mock,Python,Unit Testing,Mocking,Tdd,Python Mock,我可以看到两种不同的方法将模拟注入到我想要测试的python代码中: 依赖项注入: 允许将协作类传递到被测试对象的构造函数中,并传入模拟对象(以及必要时的工厂,如Java) 猴子修补: 用一个模拟对象工厂(mock object factory)剔除测试模块中的协作类(这样构造协作类实际上会创建一个模拟对象)。我不需要允许它们通过构造函数注入或创建任何工厂 python模拟库(例如,或)似乎支持这两种方法。我应该在Python中使用哪种方法,是明智的还是有更好的方法 尽可能多地使用DI通常是有用

我可以看到两种不同的方法将模拟注入到我想要测试的python代码中:

  • 依赖项注入:

    允许将协作类传递到被测试对象的构造函数中,并传入模拟对象(以及必要时的工厂,如Java)

  • 猴子修补:

    用一个模拟对象工厂(mock object factory)剔除测试模块中的协作类(这样构造协作类实际上会创建一个模拟对象)。我不需要允许它们通过构造函数注入或创建任何工厂


  • python模拟库(例如,或)似乎支持这两种方法。我应该在Python中使用哪种方法,是明智的还是有更好的方法

    尽可能多地使用DI通常是有用的,但有时它就是不可行, 因为你:

    • 使用内置函数或对象(如文件)
    • 第三方职能
    • 使用非确定性对象/调用等
    那是你不得不求助于猴子修补术的时候

    在几乎所有情况下,你都应该能够避免它,理论上,你可以100%避免它,但有时候,让猴子补丁例外更合理。

    注意:这个问题的任何答案都同样适用于传递其他类型的双精度(模拟、伪造、存根等)

    关于这个话题有很多宗教信仰,所以这个问题的标准答案是:“做对你的应用程序实用的事情,利用你的代码嗅觉。”虽然我也倾向于拒绝“非此即彼”的方法,但这个答案对任何真正问这个问题的人(即我自己)来说都是毫无用处的。以下是我正在使用的决策过程,以及我在开发过程中考虑的一些事项:

    事先评估
  • 你的代码能从更多的函数式编程中获益吗?如果你能排除依赖性和副作用,你不需要模仿任何东西
  • 单元是否可以进行有意义的单元测试?也许它需要重构,或者可能只是无法进行有效的单元测试的“粘合代码”(但必须由系统测试覆盖)
  • 您真的需要模拟依赖关系吗?如果运行真正的代码仍然允许您有意义地测试行为,并且既不慢也不破坏,那么就让它运行吧
  • 嘲弄策略
    • 如果特定的依赖关系是针对单元的,则在产品和测试中的monkey patch中对其进行硬编码。
      • 异常:如果在测试中使用生产依赖项可能对系统有害,请将其作为位置参数注入
      • 例外情况:如果存在将monkey补丁应用于另一个单元依赖项的风险,并且这是不可取的,则将其作为位置参数注入
    • 如果相同类型的依赖项对单元至关重要,则将其作为必需的位置参数注入
    • 如果依赖项对单元不是必需的,则将其作为可选的关键字参数注入

    术语 依赖注入:在Python上下文中,这个术语通常是专门指

    Monkey Patching:(在测试代码中)在运行时与模块中绑定的对象不同。在实践中,这通常意味着使用

    例子 假设我们有一个在测试过程中不受欢迎的带有副作用的函数,无论是破坏性的(向生产数据库写入无意义的内容)还是恼人的(例如,速度慢)。下面是后一种情况及其测试的示例:

    def foo():
        ...
        time.sleep(5)
        ...
        return "bar"
    
    ...
    
    def foo_test():
        assertEqual(foo(), "bar")
    
    我们的测试正常,但至少需要5秒钟。我们可以通过替换
    时间来避免等待。这两种策略是本问题的主题:

    依赖注入 猴子修补术 为什么依赖注入? 我发现猴子补丁的优点和缺点更简单,所以我将分析重点放在依赖注入上

    混乱的接口只是为了可测试性? 依赖项注入非常明确,但需要修改产品代码。猴子补丁并不明确,但不需要修改产品代码

    程序员的本能反应是在修改产品代码进行测试之前做出许多牺牲。在考虑了注入所有依赖项后应用程序的外观之后,对monkey补丁的偏好似乎是不言而喻的。作为:

    [E] ven内部API仍然需要开发人员和我 我也不喜欢和他们鬼混

    我的论点是依赖注入只是为了可测试性 Python从来都不需要,也很少比其他语言更可取 技术。依赖注入在很多情况下都很重要 不过,作为一种结构/架构,它本身很有用

    当编写单元测试时,这个话题自然而然地就出现了,但对那些提倡依赖注入的人的一种慈善解释认为,可测试性不是他们的主要动机。这种猴子补丁“让代码有点难以理解,我更愿意在某个地方看到:我们现在正在测试,所以这就是你获得时间的方式。”他详细阐述(3:14):

    当我坐下来看我的代码时,我会想,‘我怎样才能更好地测试它?’我会改变产品代码,使其更易于测试,实际上这是更好的产品代码。我认为这是因为,如果你必须写一些能做一件事的东西,它能把一件事做好,但是如果你写一些能把两件事做好的东西,那是一件更好的事情。做好测试,除了成为一个好的产品,还能使代码变得更好。通过有两种用途,您必须真正考虑API,并且必须真正考虑这一段代码在做什么。通过让它很好地完成这两项工作,你得到了一个更好的、模块化的,
    def foo(wait=time.sleep):
        ...
        wait(5)
        ...
        return "bar"
    
    ...
    
    def foo_test():
        assertEqual(foo(wait=mock.Mock()), "bar")
    
    def foo():
        ...
        time.sleep(5)
        ...
        return "bar"
    
    ...
    
    @mock.patch('time.sleep')
    def foo_test():
        assertEqual(foo(), "bar")
    
    -- bar = foo()
    ++ bar = foo(wait=mywait)
    
    -- def foo(wait=time.sleep):
    ++ def foo():
    
    class OldClass(object):
        ...
        def EnLolCat(self, misspelt_phrases):
            lolcats = []
            for album in self.albums:
                for photo in album.Photos():
                    exif = photo.EXIF()
                    for text in misspelt_phrases:
                        geoText = text.Geo(exif)
                        if photo.canCat(geoText)
                            lolcat = photo.Cat(geoText)
                            self.lolcats = lolcats
    
    def Lol(albums, misspelt_phrases):
        lolcats = []
        for album in albums:
            for photo in album.Photos():
                exif = photo.EXIF()
                for text in misspelt_phrases:
                    geoText = text.Geo(exif)
                    if photo.canCat(geoText)
                        lolcat = photo.Cat(geoText)
                        return lolcats
    
    class NewClass(object):
        ...
        def EnLolCat(self, misspelt_phrases):
            self.lolcats = Lol(
                 self.albums, misspelt_phrases)