【问题标题】:How to create a unittest for a function that uses http client library using pytest & mocks in python?如何在 python 中使用 pytest 和 mocks 为使用 http 客户端库的函数创建单元测试?
【发布时间】:2020-08-13 01:27:47
【问题描述】:

如何使用 pytest 模拟测试以下函数?

import http.client

def get_response(req_type, host, sub_domain, payload=None, headers=None,
                 body=None):

    conn = http.client.HTTPSConnection(host)
    conn.request(req_type, sub_domain, headers=headers, body=payload)
    response = conn.getresponse()

    if response.status != 200:
        raise Exception('invalid http status ' + str(response.status)
                        + ',detail body:' + response.read().decode("utf-8"))

    data = response.read().decode("utf-8")
    conn.close()

    return data

通过使用 pytest_mock 库并参考代码 here,我能够对使用 get_response() 执行某些操作的其他函数进行单元测试。因此,如果我们甚至需要使用 pytest_mock 库来执行 get_response 的单元测试,那就没问题了

话虽如此,到目前为止我看到的解决方案都是针对 requestsunittest 库的

我想避免在这个基于模拟的单元测试中创建 http 服务器或烧瓶服务器。

Pytest documentation 似乎暗示我需要为 http.client.HTTPSConnectionconn.request()conn.getresponse() 提供补丁以对 get_response() 进行单元测试。是这样吗?

一个最小的工作示例会很有帮助。提前致谢!

【问题讨论】:

    标签: python unit-testing mocking pytest pytest-mock


    【解决方案1】:

    这是一个最小的工作示例(假设您的方法放在http_call.py):

    from http_call import get_response
    
    
    def test_get_response(mocker):
      # mocked dependencies
      mock_HTTPSConnection = mocker.MagicMock(name='HTTPSConnection')
      mocker.patch('http_call.http.client.HTTPSConnection', new=mock_HTTPSConnection)
      mock_HTTPSConnection.return_value.getresponse.return_value.status = 200
      mock_HTTPSConnection.return_value.getresponse.return_value.read.return_value.decode.return_value = "some html goes here"
    
      # act
      data = get_response("GET", "www.google.com", '/', headers={})
    
      # assert
      assert "some html goes here" == data
    

    示例说明:

    1. 您模拟了 https 连接的“根”,即HTTPSConnection 类。然后你必须确保你的响应是 200 并返回一些你可以断言的数据。
    2. 然后调用函数并获取输出
    3. 然后您可以使用已模拟的预定义硬编码值断言输出

    你可以在这个测试函数中做一些额外的事情:

    使用我的pytest-mock-generator 库夹具添加断言,如下所示:

    def test_get_response(mocker, mg):
        # mocked dependencies
        mock_HTTPSConnection = mocker.MagicMock(name='HTTPSConnection')
        mocker.patch('http_call.http.client.HTTPSConnection', new=mock_HTTPSConnection)
        mock_HTTPSConnection.return_value.getresponse.return_value.status = 200
        mock_HTTPSConnection.return_value.getresponse.return_value.read.return_value.decode.return_value = "some html goes here"
    
        # act
        data = get_response("GET", "www.google.com", '/', headers={})
    
        # assert
        assert "some html goes here" == data
    
        # this code generates extra asserts
        mg.generate_asserts(mock_HTTPSConnection)
    

    这将生成输出(打印到控制台并复制到剪贴板):

    assert 1 == mock_HTTPSConnection.call_count
    mock_HTTPSConnection.assert_called_once_with('www.google.com')
    mock_HTTPSConnection.return_value.request.assert_called_once_with('GET', '/', body=None, headers={})
    mock_HTTPSConnection.return_value.getresponse.assert_called_once_with()
    mock_HTTPSConnection.return_value.getresponse.return_value.read.assert_called_once_with()
    mock_HTTPSConnection.return_value.getresponse.return_value.read.return_value.decode.assert_called_once_with('utf-8')
    mock_HTTPSConnection.return_value.close.assert_called_once_with()
    

    这些额外的断言可以帮助您确保使用正确的参数调用函数。

    我的库可以做的另一件事是通过分析您的代码来生成初始模拟,如下所示:

    def test_get_response(mocker, mg):
        mg.generate_uut_mocks(get_response)
    

    你会得到这个输出:

    # mocked dependencies
    mock_HTTPSConnection = mocker.MagicMock(name='HTTPSConnection')
    mocker.patch('http_call.http.client.HTTPSConnection', new=mock_HTTPSConnection)
    mock_Exception = mocker.MagicMock(name='Exception')
    mocker.patch('http_call.Exception', new=mock_Exception)
    mock_str = mocker.MagicMock(name='str')
    mocker.patch('http_call.str', new=mock_str)
    

    显然不需要模拟Exceptionstr,所以你可以放弃这些建议。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-03
      • 1970-01-01
      • 1970-01-01
      • 2015-01-24
      • 1970-01-01
      • 1970-01-01
      • 2019-10-25
      • 1970-01-01
      相关资源
      最近更新 更多