pytest 夹具返回一个类,其中使用 requests.request 的结果保存在 json 文件中

问题描述 投票:0回答:1

我有一个 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
函数中,它永远不会执行该部分。我怎样才能做到这一点?

python testing pytest
1个回答
0
投票

这里有两个问题:

  1. 您正在以无操作的方式应用补丁
  2. 你正在修补错误的东西

错误的补丁逻辑

您正在对

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

© www.soinside.com 2019 - 2024. All rights reserved.