python - 使用 patch() 来模拟我没有明确导入的东西
问题描述
假设我有以下模块:
# src/myapp/utils.py
import thirdparty
from myapp.secrets import get_super_secret_stuff
def get_thirdparty_client() -> return thirdparty.Client:
thirdparty.Client(**get_super_secret_stuff())
# src/myapp/things.py
from myapp.utils import get_thirdparty_client
from myapp.transformations import apply_fairydust, make_it_sparkle
def do_something(x):
thirdparty_client = get_thirdparty_client()
y = thidparty_client.query(apply_fairydust(x))
return make_it_sparkle(y)
假设这myapp
是经过轻微测试的遗留代码,重构是不可能的。还假设(令人讨厌)在其方法中thirdparty.Client
执行非确定性网络 I/O 。__init__
因此,我打算模拟thirdparty.Client
类本身,以使该do_something
功能可测试。
还假设我必须使用unittest
并且不能使用像 Pytest 这样的另一个测试框架。
看起来patch
函数 fromunittest.mock
是适合这项工作的工具。但是,我不确定如何将通常的警告应用于“使用补丁的地方”。
理想情况下,我想编写一个看起来像这样的测试:
# tests/test_myapp/test_things.py
from unittest import TestCase
from unittest.mock import patch
from myapp.things import do_something
def gen_test_pairs():
# Generate pairs of expected inputs and outputs
...
class ThingTest(unittest.TestCase):
@patch('????')
def test_do_something(self, mock_thirdparty_client):
for x, y in gen_test_pairs:
with self.subTest(params={'x': x, 'y': y}):
mock_thirdparty_client.query.return_value = y
self.assertEqual(do_something(x), y)
我的问题是我不知道该写什么来代替,????
因为我从来没有真正导入thirdparty.Client
.src/myapp/things.py
我考虑的选项:
- 在 处应用补丁
myapp.utils.thirdparty.Client
,这使我的测试变得脆弱并依赖于实现细节。 - “打破规则”并在
thirdparty.Client
. get_thirdparty_client
在测试中导入,在其上使用patch.object
,并将其设置return_value
为我单独创建的另一个MagicMock
,第二个模拟将代表thirdparty.Client
. 这使得更冗长的测试代码不能轻易地用作单个装饰器。
这些选项听起来都不是特别吸引人,但我不知道哪个被认为是最不糟糕的。
或者是否有其他我没有看到的选项可供我使用?
解决方案
正确答案是 2:将补丁应用于thirdparty.Client
.
这是正确的,因为它实际上并没有破坏“使用它的补丁”规则。
该规则旨在是描述性的,而不是文字的。在这种情况下,thirdparty.Client
被认为是在thirdparty
模块中“使用”。
这个概念在 Lisa Roach 的 2018 PyCon 演讲“Demystifying the Patch Function”中有更详细的描述。完整的录音可在 YouTube 上找到:https ://youtu.be/ww1UsGZV8fQ?t=537 。这个特殊案例的解释从视频大约 9 分钟开始。
推荐阅读
- atom-editor - 为什么我的 Atom Text Editor 字体现在变得更小更粗了?
- python - 无法在 Raspberry Pi 3 上通过 pip3 安装 opencv-python
- php - 如何在 laravel 5 中发送电子邮件(没有 Swiftmailer)?
- .net-core - 为什么 Httpclient 在带有 401 的 .NET Core 上失败,但在 .NET Framework 中工作(并且只能通过 VPN)?
- excel - 如何从电源查询将参数传递给 sql 查询
- angular - @ngrx/entity 如何更新多个级别的项目
- vue.js - 如何从组件数据中传递 vue-router 中的多个参数
- angular - 如何检查同一元素上是否存在另一个指令
- django - 覆盖 LogoutView.get
- reactjs - React Virtualized Table - 约 8 列表中的输入元素的性能问题