跳转到主要内容

轻松以隔离方式导入模块并模拟其依赖项。

项目描述

Python导入模拟器

Python导入模拟器提供了一个轻松导入模块并以隔离方式模拟其依赖项的方法。依赖项的模拟将仅应用于当前导入,而不会影响全局模块命名空间。

快速入门

安装

pip install import-mocker

模拟导入

from import_mocker import ImportMocker

modules_to_mock = ['B', 'C']
imocker = ImportMocker(modules_to_mock)
A = imocker.import_module('A')

验证模拟模块的行为

mocks = imocker.get_mocks()
b_mock = mocks['B']
b_mock.some_method.assert_called()

重置模拟模块

imocker.reset_mock('B')
imocker.reset_mocks()

在模拟模块上下文中执行代码

当要执行的代码将执行内联导入时,这很有用。

imocker.execute(lambda: function_that_calls_inline_import(x, y, z=4))

理由

在Python中进行单元测试时,我们无法找到一种无需影响全局作用域并且无需仔细模拟和取消模拟导入模块的简单模拟导入方法。

这对我们来说是个问题,因为我们需要测试一些文件,然后在测试其他文件时模拟相同的文件,而我们无法控制测试执行的顺序。以下是一个示例

# ***** SOURCE CODE *****
# FILE: A.py
import B
import C
import D
...

# FILE: B.py
import C
import D
...


# ***** TESTS *****
# FILE: test_a.py
# We need to mock B, and C, and the real version of D
from importlib import reload
from unittest import mock
sys.modules["B"] = mock.Mock()
sys.modules["C"] = mock.Mock()

import D
D = reload(D) # Make sure we get the real module if D was mocked before

import A # this line recursively imports B, C, and D
A = reload(A) # Make sure the correct mocks are used if A was mocked before

...

# FILE: test_b.py
# We need to mock only D, and need the real version of C
from importlib import reload
from unittest import mock
sys.modules["D"] = mock.Mock()

import C # Make sure the correct mocks are used if C was mocked before
C = reload(C)
import B # Make sure the correct mocks are used if B was mocked before
B = reload(B)

如所见,这可能会变得非常冗长,特别是当依赖关系开始增长并且我们需要不同的模拟配置时。

这就是我们创建Python Import Mocker的原因,为了极大地简化这个过程,无需每次都重新发明轮子。希望您觉得它像我们一样有用😀。

示例

参考上一节提供的示例,以下是Python Import Mocker的使用方法

# ***** SOURCE CODE *****
# FILE: A.py
import B
import C
import D
...

# FILE: B.py
import C
import D
...


# ***** TESTS USING PYTHON IMPORT MOCKER *****
# FILE: test_a.py
# We need to mock B and C
from unittest import mock
from import_mocker import ImportMocker

modules_to_mock = ['B', 'C']
imocker = ImportMocker(modules_to_mock)
A = imocker.import_module('A')
...

def my_test_01():
    # Do something and verify B and C
    imocker.reset_mocks()
    execute_code()
    mocks = imocker.get_mocks()
    b_mock = mocks['B']
    b_mock.some_method.assert_called()
    ...
    
    # Do something else and verify C
    imocker.reset_mock('C')
    execute_mode_code()
    c_mock = imocker.get_mock('C')
    c_mock.some_method.assert_called_once()
    ...
...

# FILE: test_b.py
# We need to mock only D, and need the real version of C
from import_mocker import ImportMocker

modules_to_mock = ['D']
imocker = ImportMocker(modules_to_mock)
B = imocker.import_module('B')
...

注意:您可以在测试文件中找到更多实用示例。

API

以下是ImportMocker类提供的函数。

import_module(module_to_import: str)

在返回模拟模块的上下文中导入module_to_import,其他所有导入都将正常工作。

如果之前已导入module_to_import,则重新加载它,以便可以再次模拟其导入的模块。

import_modules(modules_to_import: List[str])

import_module具有相同的逻辑,但接收要导入的模块名称列表,并返回相同顺序的导入模块列表。

execute(function, *args, **kwargs)

在导入时返回模拟模块的上下文中执行函数,其他所有导入都将正常工作。*args**kwargs是要传递给function的参数。

这在测试具有import语句的函数中的代码时非常有用,您想模拟这些导入。

重要提示:如果模块是在当前ImportMocker实例之外导入的,则在执行函数时不会重新导入。

get_mocks()

获取包含所有模拟模块的字典的副本。

get_mock(mock_name: str)

获取指定的模拟模块。

reset_mocks()

将所有模拟模块重置为其原始状态。

reset_mock(mock_name: str)

将指定的模拟模块重置为其原始状态。

贡献

该项目欢迎贡献和建议。大多数贡献都需要您同意贡献者许可协议(CLA),声明您有权并且实际上确实授予我们使用您的贡献的权利。有关详细信息,请访问https://cla.opensource.microsoft.com

当您提交拉取请求时,CLA机器人将自动确定您是否需要提供CLA,并相应地装饰PR(例如,状态检查,评论)。只需遵循机器人提供的说明。您只需在整个使用我们的CLA的仓库中这样做一次。

该项目采用了Microsoft开源行为准则。有关更多信息,请参阅行为准则FAQ或通过opencode@microsoft.com联系以获取任何其他问题或评论。

商标

该项目可能包含项目、产品或服务的商标或徽标。Microsoft商标或徽标的授权使用必须遵守并遵循Microsoft的商标和品牌指南。在修改此项目的版本中使用Microsoft商标或徽标不得引起混淆或暗示Microsoft的赞助。任何第三方商标或徽标的使用均受这些第三方政策约束。

项目详情


下载文件

下载适合您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源分布

import-mocker-0.2.0.tar.gz (5.3 kB 查看散列)

上传于 源代码

构建分发

import_mocker-0.2.0-py2.py3-none-any.whl (5.9 kB 查看哈希值)

上传于 Python 2 Python 3

由以下机构支持