python 用于测试请求和响应的 pytest

python pytest for testing the requests and response

我是在 python 中使用 pytest 的初学者,并尝试为以下方法编写测试用例,当传递正确的 ID 时获取用户地址,否则会出现自定义错误 BadId。

    def get_user_info(id: str, host='127.0.0.1', port=3000 ) -> str:
     uri = 'http://{}:{}/users/{}'.format(host,port,id)
     result = Requests.get(uri).json()
     address = result.get('user',{}).get('address',None)
     if address:
       return address
     else:
       raise BadId

有人可以帮我解决这个问题吗?你能建议我学习 pytest 的最佳资源是什么吗? TIA

您的测试方案可能看起来像这样。

首先,我建议创建一个夹具以用于各种方法测试。 fixture 设置了一个 class 的实例以用于您的测试,而不是在测试本身中创建该实例。以这种方式将任务分开有助于使您的测试更健壮且更易于阅读。

from my_package import MyClass
import pytest

@pytest.fixture
def a_test_object():
    return MyClass()

您可以将测试对象传递给您的一系列方法测试:

def test_something(a_test_object):
    # do the test

但是,如果您的测试对象在设置期间需要一些资源(例如连接、数据库、文件等),您可以模拟它来避免为测试设置资源。有关如何执行此操作的一些有用信息,请参阅 this talk

顺便说一句:如果您需要测试在您的夹具中创建的用户定义对象的多个不同状态,您将需要参数化您的夹具。这是一个有点复杂的话题,但是文档 explains fixture parametrization very clearly.

您需要做的另一件事是确保拦截对 Requests 的任何 .get 调用。这很重要,因为它允许您的测试 运行 没有互联网连接,并确保它们不会因连接不良而失败,这不是您要测试的东西。

你可以使用pytestmonkeypatch feature拦截Requests.get。所需要的只是将 monkeypatch 作为测试方案函数的输入参数。

您可以使用另一个夹具来完成此操作。它可能看起来像这样:

import Requests
import pytest

@pytest.fixture
def patched_requests(monkeypatch):
    # store a reference to the old get method
    old_get = Requests.get
    def mocked_get(uri, *args, **kwargs):
        '''A method replacing Requests.get
        Returns either a mocked response object (with json method)
        or the default response object if the uri doesn't match
        one of those that have been supplied.
        '''
        _, id = uri.split('/users/', 1)
        try:
            # attempt to get the correct mocked json method
            json = dict(
            with_address1 = lambda: {'user': {'address': 123}},
            with_address2 = lambda: {'user': {'address': 456}},
            no_address = lambda: {'user': {}},
            no_user = lambda: {},
            )[id]
        except KeyError:
            # fall back to default behavior
            obj = old_get(uri, *args, **kwargs)
        else:
            # create a mocked requests object
            mock = type('MockedReq', (), {})()
            # assign mocked json to requests.json
            mock.json = json
            # assign obj to mock
            obj = mock
        return obj
    # finally, patch Requests.get with patched version
    monkeypatch.setattr(Requests, 'get', mocked_get)

这看起来很复杂,直到您了解正在发生的事情:我们只是制作了一些具有预先确定的用户 ID 和地址的模拟 json 对象(由字典表示)。 Requests.get 的补丁版本只是 returns 一个类型为 MockedReq 的对象,在请求其 id 时使用相应的模拟 .json() 方法。

请注意 Requests 只会在实际使用上述夹具的测试中进行修补,例如:

def test_something(patched_requests):
    # use patched Requests.get

任何不使用 patched_requests 作为输入参数的测试都不会使用补丁版本。

另请注意,您可以在测试本身内进行 monkeypatch Requests,但我建议单独进行。如果您正在使用请求的其他部分 API,您可能还需要对它们进行 monkeypatch。将所有这些东西分开通常比将其包含在测试中更容易理解。

接下来写你的各种方法测试。您需要对方法的各个方面进行不同的测试。换句话说,您通常会为您的方法成功的实例编写不同的测试,并在失败时编写另一个测试。

首先我们用几个测试用例测试方法是否成功。

@pytest.mark.parametrize('id, result', [
    ('with_address1', 123),
    ('with_address2', 456),
])
def test_get_user_info_success(patched_requests, a_test_object, id, result):
    address = a_test_object.get_user_info(id)
    assert address == result

接下来我们可以使用 with pytest.raises 功能测试引发 BadId 异常。请注意,由于引发了异常,因此测试函数没有 result 输入参数。

@pytest.mark.parametrize('id', [
    'no_address',
    'no_user',
])
def test_get_user_info_failure(patched_requests, a_test_object, id):
    from my_package import BadId
    with pytest.raises(BadId):
        address = a_test_object.get_user_info(id)

如我的评论中所述,这里还有一些额外的资源可以帮助您了解有关 pytest 的更多信息:

link

link

另外一定要查看 Brian Okken's book and Bruno Oliveira's book。都对学习pytest很有帮助