我有一个 ApiClient 的固定装置。使用 pytest 标志,我希望有一个条件,其中如果传递
--record
标志,则夹具中返回的 ApiClient 会修改 requests.request
的用法,以便将请求的结果记录在 .json 文件中。
这是我得到的代码,但它不起作用
def record_requested_data(*args, **kwargs):
response = requests.request(*args, **kwargs)
# Save the response in a JSON file
record_path = os.path.join("recorded_responses", f"{args[0].replace('/', '_')}.json")
with open(record_path, 'w') as record_file:
json.dump(response.json(), record_file)
return response
@pytest.fixture(scope="function")
def api_client(record_mode: bool):
if record_mode:
api_client = get_api_client()
with pytest.MonkeyPatch().context() as m:
m.setattr(requests, 'request', record_requested_data)
return mp_api_client
else:
return FakeAPIClient()
当我使用该标志运行测试时,它会进入
if record_mode
条件(因此该标志正在工作),但如果我将调试器放入 record_requested_data
函数中,它永远不会执行该部分。我怎样才能做到这一点?
这里有两个问题:
您正在对
requests
模块进行猴子修补,如下所示:
api_client = get_api_client()
with pytest.MonkeyPatch().context() as m:
m.setattr(requests, 'request', record_requested_data)
return mp_api_client
您正在使用
pytest.monkeypatch.context
机制,这意味着当上下文退出时,您所做的任何补丁都会被删除。您的 return mp_api_client
语句位于 with
块之外,这意味着此时所有补丁都已被清理。
即使你将
return
语句移到with
块内,它仍然是无效的:当函数退出时,上下文将被清理。
yield
夹具,如下所示:
def api_client():
api_client = get_api_client()
with pytest.MonkeyPatch().context() as m:
m.setattr(requests, 'request', record_requested_data)
yield mp_api_client
像这样使用
yield
意味着在测试中使用夹具时上下文保持活动状态,并且仅在测试完成时才清理。
您已经修补了
requests.request
,但是如果您检查 requests
模块的源代码,request
函数(以及 get
、post
等)都在 requests.api
中定义。修补 requests.request
实际上是无操作,除非您明确调用 requests.request
。您正在调用 requests.get
,它将运行 requests.api.get
。
这是您的测试的有效版本。由于您的问题没有提供完整的重现器,我简化了一些事情并弥补了其他事情,但我认为这应该足以演示解决方案:
import pytest
import requests
class ApiClient:
def get_data(self):
res = requests.get("https://jsonplaceholder.typicode.com/todos/1")
return res.json()
def record_requested_data(*args, **kwargs):
response = requests.request(*args, **kwargs)
# Save the response in a JSON file
with open("requestlog.txt", "w") as record_file:
record_file.write(response.text)
return response
@pytest.fixture(scope="function")
def api_client():
api_client = ApiClient()
with pytest.MonkeyPatch().context() as m:
m.setattr(requests.api, "request", record_requested_data)
yield api_client
def test_apiclient(api_client):
api_client.get_data()
请注意,这里我们显式地利用了以下事实:
requests.request
持有对unpatched函数的引用;如果不是这种情况,最终只会递归调用 record_requested_data
。