当前位置: 首页 > 面试题库 >

在Python中,如何从另一个未本地导入的文件中修补功能?

彭礼骞
2023-03-14
问题内容

我正在学习Pythonic测试开发,偶然发现了这个看似反直觉的问题。当我修补与被测代码在同一文件中定义的函数时,该函数patch可以正常工作。但是,当我import使用其他文件中的函数时,唯一能够patch正常工作的方法是将import本地化,而不是在文件顶部定义它。

最小复制量:

a / b.py:

from x.y import z


def c():
    print("In a.b.c")


class D:
    def do_stuff_with_a_b_c(self):
        print("In do_stuff_with_a_b_c")
        c()

    def do_stuff_with_x_y_z(self):
        from x.y import z
        print("In do_stuff_with_x_y_z")
        z()

x / y.py:

def z():
    print("In x.y.z")

测试/d_tests.py:

import inspect
import unittest
from unittest.mock import patch

from x import y
from a.b import D


class DTests(unittest.TestCase):
    def test_do_stuff_with_a_b_c(self):
        print(f"In {inspect.stack()[0][3]}")
        D().do_stuff_with_a_b_c()

    @patch("a.b.c")
    def test_do_stuff_with_patched_a_b_c(self, a_b_c_method):
        print(f"In {inspect.stack()[0][3]}")
        D().do_stuff_with_a_b_c()

    def test_do_stuff_with_x_y_z(self):
        print(f"In {inspect.stack()[0][3]}")
        D().do_stuff_with_x_y_z()

    @patch("x.y.z")
    def test_do_stuff_with_patched_x_y_z(self, x_y_z_method):
        print(f"In {inspect.stack()[0][3]}")
        D().do_stuff_with_x_y_z()

    def test_do_stuff_with_patch_object_x_y_z(self):
        print(f"In {inspect.stack()[0][3]}")
        with patch.object(y, "z"):
            D().do_stuff_with_x_y_z()


if __name__ == '__main__':
    unittest.main()

当我使用上面的代码运行测试时,得到以下输出:

In test_do_stuff_with_a_b_c
In do_stuff_with_a_b_c
In a.b.c
In test_do_stuff_with_patch_object_x_y_z
In do_stuff_with_x_y_z
In test_do_stuff_with_patched_a_b_c
In do_stuff_with_a_b_c
In test_do_stuff_with_patched_x_y_z
In do_stuff_with_x_y_z
In test_do_stuff_with_x_y_z
In do_stuff_with_x_y_z
In x.y.z

但是,当我注释掉x.y.zin的本地导入时do_stuff_with_x_y_z,我得到以下输出:

In test_do_stuff_with_a_b_c
In do_stuff_with_a_b_c
In a.b.c
In test_do_stuff_with_patch_object_x_y_z
In do_stuff_with_x_y_z
In x.y.z
In test_do_stuff_with_patched_a_b_c
In do_stuff_with_a_b_c
In test_do_stuff_with_patched_x_y_z
In do_stuff_with_x_y_z
In x.y.z

导致patch在一种情况下按预期工作但在另一种情况下无法工作的两种形式之间有什么区别?


问题答案:

修补时,顶级和本地导入这两种情况必须以不同的方式处理。您必须按照文档中或Ned
Batchelder的本博文中的描述对模块中使用的对象进行修补,后者将更详细地描述问题。

在第一种情况下,您需要在修补模块 z在运行时导入模块,以便导入修补后的模块。在第二种情况(顶级导入的更标准的情况)中,您 修补模块
之前先 导入模块,现在在模块中已有未修补的引用,因此要使修补起作用,您必须修补 此引用 z``b __

@patch('a.b.z')
def test_do_stuff_with_patched_x_y_z(self, mocked_z):
   ...

总结一下:您始终必须检查要使用的对象如何在要测试的模块中导入。基本上有两种情况:

  • 该对象的导入方式为import ximport x.y(和使用方式类似x.y.z)-在这种情况下,可以将其修补为x.y.z。如果像在最初的情况那样在函数内部本地导入模块,也是如此。
  • from x.y import z(或from .y import z)一样导入对象-在这种情况下,将创建引用该对象的本地引用,并且应对该引用进行修补(例如,a.b.z在您的情况下)


 类似资料:
  • 我的项目目录如下所示: 在我的主要范围内。py,我从文档导入一个函数。py如下: 它工作正常。 如何在中导入相同的东西?我尝试了: 但我有一个错误:

  • 问题内容: 我在用另一个功能替换另一个模块中的功能时遇到麻烦,这让我发疯。 假设我有一个看起来像这样的模块bar.py: 我还有另一个看起来像这样的模块: 我希望得到结果: 但是我得到了这个: 我究竟做错了什么? 问题答案: 考虑一下Python名称空间的工作原理可能会有所帮助:它们实际上是字典。因此,当您执行此操作时: 这样想: 希望您能明白为什么它不起作用:-)将名称导入名称空间后, 从中 导

  • 问题内容: 我正在使用其他人编写的模块。我想猴子修补模块中定义的类的方法。我发现的示例显示了如何执行此操作的所有示例均假设我自己将自己称为该类(例如Monkey-patch Python类)。然而,这种情况并非如此。在我的情况下,该类是在另一个模块的函数中初始化的。请参阅下面的(大大简化的)示例: thirdpartymodule_a.py thirdpartymodule_b.py mymodu

  • 问题内容: 我只是想从另一个文件中加入我的Swift类,例如它的测试 PrimeNumberModel.swift PrimeNumberModelTests.swift 两个swift文件都在同一目录中。 问题答案: 我的文件中也遇到了同样的问题,但常规项目文件中却没有。 摆脱: 使用未解析的标识符“ PrimeNumberModel” 我需要测试文件中的基本模块。在我的情况下,我的 目标 称为

  • 问题内容: 我在编写Python程序很有趣,但在尝试从另一个文件中的类导入函数时遇到问题。这是我的代码: 我想返回一个从另一个文件中的类调用的函数。当我导入文件时,它首先运行其中的类,然后继续运行原始代码。为什么会这样? 这是comm_system的代码: 问题答案: 将通讯系统的结尾更改为: 总是在运行的那些行会导致它在导入和执行时都运行。

  • 问题内容: 在将其标记为重复之前, 请阅读我的问题: 我正在尝试从子目录的文件中导入类 并且在我的课程中有()我尝试了什么: 放入main.py: 我收到错误消息: 从文件导入Klasa ImportError:没有名为“文件”的模块 当我尝试使用时: 我收到此错误: tmp = Klasa() NameError:未定义名称“ Klasa” 我在子文件夹中放了一个空格,它仍然不起作用,而我在 :

  • 问题内容: 我有这种文件结构(目录和箭头文件之后): 主要: 型号目录: 报告目录: 现在,我想导入,但是我尝试做的错误是没有这样的模块。 我尝试了这个: 然后: 看起来这两个文件夹没有看到对方。从其他目录导入文件的方式是什么?我需要在 init .py文件中指定一些其他导入吗? 问题答案: 您可以在运行时添加到系统路径: 到目前为止,这是最简单的方法。

  • 问题内容: 我将根据一个基于不同导入规则的教程,使用Python 3.5构建Flask应用。通过寻找类似的问题,我设法通过将文件夹添加到路径来解决从嵌套文件夹导入的ImportError,但是我仍然无法从同一文件夹(已经在路径中)的脚本中导入函数。文件夹结构是这样的: 在app.py中,我使用以下代码从config.py导入了一个函数: 但是我得到这个错误: 我不明白这是什么问题,因为这两个文件位