python pytest用于测试请求和响应

lan*_*pta 6 python tdd pytest python-3.x

我是初学者在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
Run Code Online (Sandbox Code Playgroud)

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

Ric*_*ica 11

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

首先,我建议创建一个用于各种方法测试的夹具.fixture将设置一个类的实例,以便在测试中使用,而不是在测试中创建实例.以这种方式保持任务分离有助于使您的测试更健壮,更易于阅读.

from my_package import MyClass
import pytest

@pytest.fixture
def a_test_object():
    return MyClass()
Run Code Online (Sandbox Code Playgroud)

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

def test_something(a_test_object):
    # do the test
Run Code Online (Sandbox Code Playgroud)

但是,如果您的测试对象在安装过程中需要一些资源(例如连接,数据库,文件等),则可以模拟它以避免为测试设置资源.有关如何执行此操作的一些有用信息,请参阅此演讲.

顺便说一句:如果您需要测试夹具中创建的用户定义对象的几种不同状态,则需要对夹具进行参数化.这是一个复杂的主题,但文档非常清楚地解释了夹具参数化.

您需要做的另一件事是确保拦截任何.get呼叫Requests.这很重要,因为它允许您的测试在没有Internet连接的情况下运行,并确保它们不会因连接错误而失败,这不是您尝试测试的内容.

您可以拦截Requests.get通过使用猴补丁功能pytest.所需要的只是包括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)
Run Code Online (Sandbox Code Playgroud)

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

请注意,Requests只会在实际使用上述灯具的测试中打补丁,例如:

def test_something(patched_requests):
    # use patched Requests.get
Run Code Online (Sandbox Code Playgroud)

任何不用patched_requests作输入参数的测试都不会使用修补版本.

另请注意,您可以Requests在测试中进行monkeypatch ,但我建议您单独进行.如果您正在使用请求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
Run Code Online (Sandbox Code Playgroud)

接下来,我们可以BadId使用该with pytest.raises功能测试是否引发异常.请注意,由于引发了异常,因此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)
Run Code Online (Sandbox Code Playgroud)

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

链接

链接