gpt4 book ai didi

python - 如何模拟需要 Response 对象的 pydantic BaseModel?

转载 作者:行者123 更新时间:2023-12-03 08:17:02 25 4
gpt4 key购买 nike

我正在为我的 API 客户端编写测试。我需要模拟 get 函数,以便它不会发出任何请求。因此,我不想返回 Response 对象,而是返回 MagicMock。但随后 pydantic 会引发 ValidationError 因为它要进入模型。

我有以下 pydantic 模型:

class Meta(BaseModel):
raw: Optional[str]
response: Optional[Response]

class Config:
arbitrary_types_allowed = True

这会引发:

>   ???
E pydantic.error_wrappers.ValidationError: 1 validation error for OneCallResponse
E meta -> response
E instance of Response expected (type=type_error.arbitrary_type; expected_arbitrary_type=Response)

一种解决方案是添加 UnionMagicMock 但我真的不想更改测试代码。事实并非如此。

class Meta(BaseModel):
raw: Optional[str]
response: Optional[Union[Response, MagicMock]]

class Config:
arbitrary_types_allowed = True

有什么想法如何修补/模拟它吗?

最佳答案

您可以创建 Response 的子类进行测试,然后修补 requests.get,而不是使用 MagicMock/Mock 返回该子类的实例。

这可以让你:

  • 将模拟类型保持为Response(让 pydantic 高兴)
  • 控制测试的大部分预期响应行为
  • 避免测试代码污染应用程序代码(是的,“一个解决方案是使用 MagicMock 添加 Union” 是 绝对不是这样的。)

(我假设 Response 来自 requests 库。如果不是,则适当调整要模拟的属性和方法。想法是一样的。)

# TEST CODE

import json
from requests import Response
from requests.models import CaseInsensitiveDict

class MockResponse(Response):
def __init__(self, mock_response_data: dict, status_code: int) -> None:
super().__init__()

# Mock attributes or methods depending on the use-case.
# Here, mock to make .text, .content, and .json work.

self._content = json.dumps(mock_response_data).encode()
self.encoding = "utf-8"
self.status_code = status_code
self.headers = CaseInsensitiveDict(
[
("content-length", str(len(self._content))),
]
)

然后,在测试中,您只需实例化一个 MockResponse 并告诉 patch 返回它:

# APP CODE

import requests
from pydantic import BaseModel
from typing import Optional

class Meta(BaseModel):
raw: Optional[str]
response: Optional[Response]

class Config:
arbitrary_types_allowed = True

def get_meta(url: str) -> Meta:
resp = requests.get(url)
meta = Meta(raw=resp.json()["status"], response=resp)
return meta
# TEST CODE

from unittest.mock import patch

def test_get_meta():
mocked_response_data = {"status": "OK"}
mocked_response = MockResponse(mocked_response_data, 200)

with patch("requests.get", return_value=mocked_response) as mocked_get:
meta = get_meta("http://test/url")

mocked_get.call_count == 1
assert meta.raw == "OK"
assert meta.response == mocked_response
assert isinstance(meta.response, Response)

关于python - 如何模拟需要 Response 对象的 pydantic BaseModel?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69041639/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com