简体   繁体   English

python pytest用于测试请求和响应

[英]python pytest for testing the requests and response

I am a beginner to using pytest in python and trying to write test cases for the following method which get the user address when correct Id is passed else rises custom error BadId. 我是初学者在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

Can someone help me with this and also can you suggest me what are the best resources for learning pytest? 有人可以帮我解决这个问题,你也可以建议我学习pytest的最佳资源是什么? TIA TIA

Your test regimen might look something like this. 您的测试方案可能看起来像这样。

First I suggest creating a fixture to be used in your various method tests. 首先,我建议创建一个用于各种方法测试的夹具。 The fixture sets up an instance of your class to be used in your tests rather than creating the instance in the test itself. fixture将设置一个类的实例,以便在测试中使用,而不是在测试中创建实例。 Keeping tasks separated in this way helps to make your tests both more robust and easier to read. 以这种方式保持任务分离有助于使您的测试更健壮,更易于阅读。

from my_package import MyClass
import pytest

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

You can pass the test object to your series of method tests: 您可以将测试对象传递给一系列方法测试:

def test_something(a_test_object):
    # do the test

However if your test object requires some resources during setup (such as a connection, a database, a file, etc etc), you can mock it instead to avoid setting up the resources for the test. 但是,如果您的测试对象在安装过程中需要一些资源(例如连接,数据库,文件等),则可以模拟它以避免为测试设置资源。 See this talk for some helpful info on how to do that. 有关如何执行此操作的一些有用信息,请参阅此演讲

By the way: if you need to test several different states of the user defined object being created in your fixture, you'll need to parametrize your fixture. 顺便说一句:如果您需要测试夹具中创建的用户定义对象的几种不同状态 ,则需要对夹具进行参数化。 This is a bit of a complicated topic, but the documentation explains fixture parametrization very clearly . 这是一个复杂的主题,但文档非常清楚地解释了夹具参数化

The other thing you need to do is make sure any .get calls to Requests are intercepted. 您需要做的另一件事是确保拦截任何对Requests .get调用。 This is important because it allows your tests to be run without an internet connection, and ensures they do not fail as a result of a bad connection, which is not the thing you are trying to test. 这很重要,因为它允许您的测试在没有Internet连接的情况下运行,并确保它们不会因连接错误而失败,这不是您尝试测试的内容。

You can intercept Requests.get by using the monkeypatch feature of pytest . 您可以拦截Requests.get使用猴补丁功能pytest All that is required is to include monkeypatch as an input parameter to the test regimen functions. 所需要的只是将monkeypatch作为测试方案函数的输入参数。

You can employ another fixture to accomplish this. 您可以使用另一个夹具来完成此任务。 It might look like this: 它可能看起来像这样:

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)

This looks complicated until you understand what is happening: we have simply made some mocked json objects (represented by dictionaries) with pre-determined user ids and addresses. 在您了解正在发生的事情之前,这看起来很复杂:我们只是使用预先确定的用户ID和地址制作了一些模拟的json对象(由字典表示)。 The patched version of Requests.get simply returns an object- of type MockedReq - with the corresponding mocked .json() method when its id is requested. 修补版本的Requests.get只返回一个类型为MockedReq的对象 - 在请求其id时使用相应的MockedReq .json()方法。

Note that Requests will only be patched in tests that actually use the above fixture, eg: 请注意, Requests仅在实际使用上述夹具的测试中进行修补,例如:

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

Any test that does not use patched_requests as an input parameter will not use the patched version. 任何不使用patched_requests作为输入参数的测试都不会使用修补版本。

Also note that you could monkeypatch Requests within the test itself, but I suggest doing it separately. 另请注意,您可以在测试中对monkeypatch Requests进行monkeypatch,但我建议您单独进行。 If you are using other parts of the requests API, you may need to monkeypatch those as well. 如果您正在使用请求API的其他部分,您可能还需要对它们进行monkeypatch。 Keeping all of this stuff separate is often going to be easier to understand than including it within your test. 保持所有这些东西分开往往比在测试中包含它更容易理解。

Write your various method tests next. 接下来写下各种方法测试。 You'll need a different test for each aspect of your method. 您需要针对方法的每个方面进行不同的测试。 In other words, you will usually write a different test for the instance in which your method succeeds, and another one for testing when it fails. 换句话说,您通常会为方法成功的实例编写不同的测试,而另一个测试则在测试失败时进行测试。

First we test method success with a couple test cases. 首先,我们使用几个测试用例测试方法成功。

@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

Next we can test for raising the BadId exception using the with pytest.raises feature. 接下来,我们可以使用with pytest.raises功能测试是否引发BadId异常。 Note that since an exception is raised, there is not a result input parameter for the test function. 请注意,由于引发了异常, result测试函数没有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)

As posted in my comment, here also are some additional resources to help you learn more about pytest: 正如我在评论中发布的,这里还有一些额外的资源可以帮助您了解有关pytest的更多信息:

link 链接

link 链接

Also be sure to check out Brian Okken's book and Bruno Oliveira's book . 另外一定要查看Brian Okken的书Bruno Oliveira的书 They are both very helpful for learning pytest. 它们对学习pytest非常有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM