如何在python中将数据模拟为request.Response类型

jac*_*118 5 python pytest python-3.x python-unittest python-unittest.mock

我想写一些测试用例来练习isinstance(obj, requests.Response) 逻辑中的object_check。在我创建 Mock 数据作为 requests.post 的返回值之后。模拟数据的类型始终是 Mock 类。这样,我如何重写模拟数据,以便模拟数据可以是 requests.Response 类型?所以我可以锻炼线d = obj.json()

from unittest.mock import patch, Mock
import unittest
import requests
from requests.exceptions import HTTPError
import pytest
def object_check(obj):
    if isinstance(obj, bytes):
        d = ujson.decode(obj.decode())
    elif isinstance(obj, requests.Response):
        d = obj.json()
    else:
        raise ValueError('invalid type')
    return d

def service_post(params):
    """
    trivial function that does a GET request
    against google, checks the status of the
    result and returns the raw content
    """
    url = "https://www.iamdomain.com"
    params = {'number': 1234, 'user_id': 1, 'name': 'john'}
    resp = requests.post(url, data=params)
    return object_check(resp)

@patch.object(requests, 'post')
def test_service_post(mock_request_post):
    data = {'number': 0000, 'user_id': 0, 'name': 'john'}
    def res():
        r = Mock()
        r.status_code.return_value = 200
        r.json.return_value = data
        return r
    mock_request_post.return_value = res()
    assert data == service_post(data)
Run Code Online (Sandbox Code Playgroud)

Mat*_*ith 12

你可以这样做:

@patch.object(requests, 'post')
def test_service_post(mock_request_post):
    data = {'number': 0000, 'user_id': 0, 'name': 'john'}
    def res():
        r = requests.Response()
        r.status_code = 200
        def json_func():
            return data
        r.json = json_func
        return r
    mock_request_post.return_value = res()
    assert data == service_post(data)
Run Code Online (Sandbox Code Playgroud)

当我在本地运行它时,测试然后通过了我。请注意,Mock 是一种迷你气味。

我曾经是Mock. 然而,随着我成长为一名开发人员,我真的试图避免它。它可能会诱使您进入一些非常糟糕的设计,并且它们真的很难维护(特别是因为您正在修改您的Mock返回值)。Mock还可能造成一种虚假的安全感(即使 Web 服务发生了巨大变化,您的测试仍将继续通过,因此您可能会在产品中爆炸)。我不认为你在这里真的需要它。两种选择:

  1. 您可以点击您想要点击的任何服务,并使用 序列化(保存)该响应pickle,然后将其存储到磁盘(将其保存在您的测试套件中)。然后让您的单元测试读回它并使用实际的响应对象。您仍然需要patch结束requests.post,但至少会为您排列返回值,并且您不必随着您的需求/应用程序的增长而添加或修改它们。
  2. 刚上网。patch完全忘记:只需在测试中执行 POST 并检查响应。当然,这可能会很慢,并且只有在您有互联网时才有效。你会遇到愚蠢的纯粹主义者,他们会告诉你永远不要在单元测试中这样做。如果您遇到那些纯粹的人之一,也许可以将其转移到集成测试中。但说真的,没有什么可以替代你在生产中实际要做的事情。这样做的好处是,如果 Web 服务发生变化,您会立即知道并修复您的代码。缺点是它会减慢您的测试套件的速度,并且它可能是一个不可靠的测试(如果 Web 服务关闭,您的测试将失败......但实际上知道这一点可能会很好)。

我建议如果 web 服务不稳定(即容易改变),使用选项 2。否则,使用选项 1。或者做一些组合(Mockpatch单元测试,并在集成测试中点击服务)。只有你能决定!

HTH,祝你好运!

  • `pickle.dump(your_object, your_file_descriptor)` 和 `pickle.load("your_filename")`。您可以在此处查看更多信息:/sf/ask/317142801/ saving-and-loading-objects-and-using-pickle 和 pickle 文档位于:https://docs.python.org/3/库/pickle.html。您需要实际访问 Web 服务,然后在“elif isinstance(obj, requests.Response):”之后插入转储逻辑。类似地,当您进行测试时,您从磁盘读回“requests.Response”对象,并将其传递到您想要运行需要它作为依赖项的单元测试的任何地方 (3认同)
  • 听到有人指出嘲笑一些你应该在投入生产之前检查的东西是多么愚蠢,这真的很令人鼓舞。我觉得我是一个疯狂的人,认为测试函数中的实时 API 调用是可以的。 (2认同)
  • @MattMessersmith 我知道你像三年前一样给出了这个解决方案,但我想感谢你。当我看到这篇文章时,我已经为此苦苦挣扎了大约 11 个小时。你救了我的培根。谢谢 (2认同)