如何模拟导入
模块 A
在其顶部包含 import B
。但是,在测试条件下,我想 mock B
在A
(模拟AB
)中并完全避免导入B
。
事实上,B
并不是故意安装在测试环境中的。
A
是被测单元。我必须导入 A
及其所有功能。 B
是我需要模拟的模块。但是我如何在 A
中模拟 B
并阻止 A
导入真正的 B
,如果第一件事 < code>A 是否导入 B
?
(未安装 B 的原因是我使用 pypy 进行快速测试,不幸的是 B 尚未与 pypy 兼容。)
这怎么办?
Module A
includes import B
at its top. However under test conditions I'd like to mock B
in A
(mock A.B
) and completely refrain from importing B
.
In fact, B
isn't installed in the test environment on purpose.
A
is the unit under test. I have to import A
with all its functionality. B
is the module I need to mock. But how can I mock B
within A
and stop A
from importing the real B
, if the first thing A
does is import B
?
(The reason B isn't installed is that I use pypy for quick testing and unfortunately B isn't compatible with pypy yet.)
How could this be done?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
您可以在导入
A
之前分配给sys.modules['B']
以获得您想要的:test.py:
A .py:
注意 B.py 不存在,但是运行
test.py
时不会返回错误,并且print(AB__name__)
打印mock_B< /代码>。您仍然需要创建一个
mock_B.py
来模拟B
的实际函数/变量/等。或者你可以直接分配一个Mock()
:test.py:
You can assign to
sys.modules['B']
before importingA
to get what you want:test.py:
A.py:
Note B.py does not exist, but when running
test.py
no error is returned andprint(A.B.__name__)
printsmock_B
. You still have to create amock_B.py
where you mockB
's actual functions/variables/etc. Or you can just assign aMock()
directly:test.py:
内置的
__import__
可以使用“mock”库进行模拟以获得更多控制:假设
A
看起来像:Aa()
返回b_mock .func()
也可以被模拟。Python 3 注释:
如3.0 的变更日志中所述,
__builtin__< /code> 现在命名为
builtins
:如果将 Python 3 的
__builtin__
替换为builtins
,则此答案中的代码可以正常工作。The builtin
__import__
can be mocked with the 'mock' library for more control:Say
A
looks like:A.a()
returnsb_mock.func()
which can be mocked also.Note for Python 3:
As stated in the changelog for 3.0,
__builtin__
is now namedbuiltins
:The code in this answer works fine if you replace
__builtin__
bybuiltins
for Python 3.很简单,只需在导入之前在 sys.modules 中模拟该库:
然后,只要
A
不依赖于从 B 的对象返回的特定类型的数据:应该可以工作。
您还可以模拟
import AB
:即使您有子模块,这也可以工作,但您需要模拟每个模块。假设你有这个:
要模拟,只需在导入包含上述内容的模块之前执行以下操作:(
我的经验:我有一个依赖项可以在一个平台 Windows 上工作,但不能在 Linux 上工作,我们在 Linux 上运行我们的日常测试。
所以我需要模拟我们测试的依赖关系。幸运的是,它是一个黑匣子,所以我不需要设置很多交互。)
模拟副作用
附录:实际上,我需要模拟一个需要一些时间的副作用。所以我需要一个对象的方法来休眠一秒钟。这会像这样工作:
然后代码需要一些时间来运行,就像真正的方法一样。
Easy, just mock the library in sys.modules before it gets imported:
and then, so long as
A
doesn't rely on specific types of data being returned from B's objects:should just work.
You can also mock
import A.B
:This works even if you have submodules, but you'll want to mock each module. Say you have this:
To mock, simply do the below before the module that contains the above is imported:
(My experience: I had a dependency that works on one platform, Windows, but didn't work on Linux, where we run our daily tests.
So I needed to mock the dependency for our tests. Luckily it was a black box, so I didn't need to set up a lot of interaction.)
Mocking Side Effects
Addendum: Actually, I needed to simulate a side-effect that took some time. So I needed an object's method to sleep for a second. That would work like this:
And then the code takes some time to run, just like the real method.
亚伦·霍尔的答案对我有用。
只是想提一件重要的事情,
如果在
A.py
中你执行from BCD import E
那么在
test.py
中你必须模拟每个模块沿着路径,否则你会得到ImportError
Aaron Hall's answer works for me.
Just want to mention one important thing,
if in
A.py
you dofrom B.C.D import E
then in
test.py
you must mock every module along the path, otherwise you getImportError
我意识到我在这里参加派对有点晚了,但是这里有一种有点疯狂的方法来使用
mock
库自动执行此操作:(这是一个示例用法)
这如此复杂的原因是当导入发生 python 基本上就是这样做的(例如
from herp.derp import foo
)sys.modules['herp']
存在吗?否则导入它。如果仍然不是ImportError
sys.modules['herp.derp']
是否存在?否则导入它。如果仍然不是ImportError
sys.modules['herp.derp']
的属性foo
。 ElseImportError
foo = sys.modules['herp.derp'].foo
这个拼凑在一起的解决方案有一些缺点:如果其他东西依赖于模块中的其他东西路径这种螺丝把它搞砸了。此外,这仅适用于内联导入的内容,例如
或
I realize I'm a bit late to the party here, but here's a somewhat insane way to automate this with the
mock
library:(here's an example usage)
The reason this is so ridiculously complicated is when an import occurs python basically does this (take for example
from herp.derp import foo
)sys.modules['herp']
exist? Else import it. If still notImportError
sys.modules['herp.derp']
exist? Else import it. If still notImportError
foo
ofsys.modules['herp.derp']
. ElseImportError
foo = sys.modules['herp.derp'].foo
There are some downsides to this hacked together solution: If something else relies on other stuff in the module path this kind of screws it over. Also this only works for stuff that is being imported inline such as
or
我找到了在 Python 中模拟导入的好方法。 此处找到了 Eric 的 Zaadi 解决方案我只是在我的 Django 应用程序中使用。
我有类
SeatInterface
,它是Seat
模型类的接口。因此,在我的
seat_interface
模块中,我有这样的导入:我想为
SeatInterface
类创建隔离测试,并将模拟的Seat
类作为FakeSeat
。问题是 - 如何在 Django 应用程序关闭的情况下离线运行测试。我有以下错误:解决方案是:
然后测试神奇地运行正常:)
I found fine way to mock the imports in Python. It's Eric's Zaadi solution found here which I just use inside my Django application.
I've got class
SeatInterface
which is interface toSeat
model class.So inside my
seat_interface
module I have such an import:I wanted to create isolated tests for
SeatInterface
class with mockedSeat
class asFakeSeat
. The problem was - how tu run tests offline, where Django application is down. I had below error:The solution was:
And then test magically runs OK :)
我知道这是一个相当老的问题,但我发现自己最近几次回到这个问题,并想分享一个简洁的解决方案。
它的工作原理是临时修补 sys.modules 中模块的密钥,然后在调用修饰函数后恢复原始模块。这可以用于测试环境中可能未安装包的场景,或者更复杂的场景,其中修补的模块实际上可能执行一些其自己的内部猴子修补(这就是我面临的情况)。
这是一个使用示例:
I know this is a fairly old question, but I have found myself returning to it a few times recently, and wanted to share a concise solution to this.
It works by temporarily patching the key for the module in
sys.modules
, and then restoring the original module after the decorated function is called. This can be used in scenarios where a package may not be installed in the testing environment, or a more complex scenario where the patched module might actually perform some of its own internal monkey-patching (which was the case I was facing).Here's an example of use:
如果您执行
import ModuleB
,您实际上是在调用内置方法__import__
:您可以通过导入
__builtin__
模块来覆盖此方法并创建一个__builtin__.__import__方法的包装器。或者您可以使用imp
模块中的NullImporter
挂钩。捕获异常并在except
块中模拟您的模块/类。指向相关文档的指针:
docs.python.org:
__import__
< /a>使用 imp 模块访问导入内部
我希望这会有所帮助。 强烈建议您进入Python编程的更神秘的领域,a)充分理解您真正想要实现的目标,b)透彻理解其含义非常重要。
If you do an
import ModuleB
you are really calling the builtin method__import__
as:You could overwrite this method by importing the
__builtin__
module and make a wrapper around the__builtin__.__import__
method. Or you could play with theNullImporter
hook from theimp
module. Catching the exception and Mock your module/class in theexcept
-block.Pointer to the relevant docs:
docs.python.org:
__import__
Accessing Import internals with the imp Module
I hope this helps. Be HIGHLY adviced that you step into the more arcane perimeters of python programming and that a) solid understanding what you really want to achieve and b)thorough understanding of the implications is important.
我今天发现自己面临着类似的问题,并且我决定以不同的方式解决它。您无需在 Python 的导入机制之上进行黑客攻击,只需将模拟模块添加到 sys.path 中,并让 Python 优先选择它而不是原始模块。
在子目录中创建替换模块,例如:
在导入
A
之前,将此目录插入到sys.path
。我正在使用 pytest,所以在我的test/conftest.py
中,我简单地完成:现在,当测试套件运行时,
mocked-lib
子目录被添加到sys.path
中,并且import A
使用B
来自模拟库
。I found myself facing a similar problem today, and I've decided to solve it a bit differently. Rather than hacking on top of Python's import machinery, you can simply add the mocked module into
sys.path
, and have Python prefer it over the original module.Create the replacement module in a subdirectory, e.g.:
Before
A
is imported, insert this directory tosys.path
. I'm using pytest, so in mytest/conftest.py
, I've simply done:Now, when the test suite is run, the
mocked-lib
subdirectory is prepended intosys.path
andimport A
usesB
frommocked-lib
.下面是使用
contextmanager
使测试看起来更清晰的一个:正在测试的函数
测试代码
Here is one using
contextmanager
to make tests look cleaner:Function being tested
test code
只是一点澄清和简化,我认为没有任何答案涵盖这一点。
假设我的应用程序类中有这些行:
...在上面我们有 3 个通常涉及导入的组件:包 (
library_core
)、该包中的模块 (configure_module)并在该模块中运行(
configure_logging
)。通常在测试中我们可能想要模拟包或模块导入失败,或者函数存在。我们可能还想模拟返回 False 的函数。
我们绝对不想做的另一件事是不安全地干扰 sys.modules,正如这里的许多答案所做的那样。 “手动”将其“恢复”到以前的状态也不够:因为测试可能会失败,因此在代码有机会执行此操作之前结束!这是基本的东西。相反,请使用上下文管理器:
以下是您如何使用
configure_logging
返回True
或False
来测试行为:...通常您可以使用
capsys
,在False
返回时将显示消息“WARN。记录器配置失败”已打印到stderr
。我认为这种嵌套修补是模拟包然后模拟其中的模块(然后是函数......)的唯一(安全)方法。
Just a bit of clarification and simplification, and I don't think any answer covers this.
Say I have these lines in my app class:
... in the above we have the 3 components typically involved in imports: package (
library_core
), module in that package (configure_module
) and function in that module (configure_logging
).Typically in the tests we might want to simulate the failure of the import of the package or the module, or the existence of the function. We might also want to simulate the function returning
False
.The other thing that we absolutely don't want to do is interfere unsafely with
sys.modules
, as so many answers here do. Nor is it sufficient to "restore" this to its former state "manually": because the test may fail and therefore end before the code gets a chance to do that! This is rudimentary stuff. Instead, use context managers:Here's how you might test on the behaviour with
configure_logging
returningTrue
orFalse
:... and typically you might use
capsys
, which onFalse
return will show that the message "WARN. Logger configuration failed" was printed tostderr
.I think that this nested patching is the only (safe) way to mock a package and then mock a module within it (and then a function ...).