目录

一、requests

二、pytest

二、请求接口获取响应

1、http发起请求的方法

1.1 requests.request方法

1.2 requests.get请求方法

1.3 requests.post请求方法

2、响应对象Response

3、发送request请求并获取响应示例(requests+pytest)

3.1 get请求

3.2 post请求

三、断言

1、普通断言

2、jsonpath断言

3、json schema断言


一、requests

    requests是一个优雅且简单的Python HTTP库,利用这个库,可以向服务器发起http请求,并获取响应结果。

    requests参考文档:https://docs.python-requests.org/en/master/

    pypi中requests库链接地址:https://pypi.org/project/requests/

    requests库安装:pip3 install requests

二、pytest

    pytest是一个功能强大的Python测试框架,它不仅支持测试用例集的管理,参数化等功能,还支持fixture,hook,插件等扩展功能。

    pytest参考文档:https://docs.pytest.org/en/latest/

    pytest.pdf文档下载地址:https://media.readthedocs.org/pdf/pytest/latest/pytest.pdf 

    pytest库安装:pip3 install pytest  

二、请求接口获取响应

1、http发起请求的方法

1.1 requests.request方法

def request(method, url, **kwargs):
    """Constructs and sends a :class:`Request <Request>`.

    :param method: method for the new :class:`Request` object: ``GET``, ``OPTIONS``, ``HEAD``, ``POST``, ``PUT``, ``PATCH``, or ``DELETE``.
    :param url: URL for the new :class:`Request` object.
    :param params: (optional) Dictionary, list of tuples or bytes to send
        in the query string for the :class:`Request`.
    :param data: (optional) Dictionary, list of tuples, bytes, or file-like
        object to send in the body of the :class:`Request`.
    :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
    :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
    :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
    :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload.
        ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')``
        or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string
        defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers
        to add for the file.
    :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth.
    :param timeout: (optional) How many seconds to wait for the server to send data
        before giving up, as a float, or a :ref:`(connect timeout, read
        timeout) <timeouts>` tuple.
    :type timeout: float or tuple
    :param allow_redirects: (optional) Boolean. Enable/disable GET/OPTIONS/POST/PUT/PATCH/DELETE/HEAD redirection. Defaults to ``True``.
    :type allow_redirects: bool
    :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
    :param verify: (optional) Either a boolean, in which case it controls whether we verify
            the server's TLS certificate, or a string, in which case it must be a path
            to a CA bundle to use. Defaults to ``True``.
    :param stream: (optional) if ``False``, the response content will be immediately downloaded.
    :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
    :return: :class:`Response <Response>` object
    :rtype: requests.Response

    Usage::

      >>> import requests
      >>> req = requests.request('GET', 'https://httpbin.org/get')
      >>> req
      <Response [200]>
    """

    # By using the 'with' statement we are sure the session is closed, thus we
    # avoid leaving sockets open which can trigger a ResourceWarning in some
    # cases, and look like a memory leak in others.
    with sessions.Session() as session:
        return session.request(method=method, url=url, **kwargs)

    参数解释:

        method:HTTP请求方法,它的值可以是:"GET","OPTIONS","HEAD","POST`","PUT","PATCH" or "DELETE"。

        url:HTTP请求访问的URL,如:"http://www.baidu.com"

        **kwargs:关键字参数;可以包含:

        params:可选参数;可以是字典,元祖列表或者字节;

        data:可选参数;可以是字典,元祖列表,字节或用于在HTTP Request请求体中发送的类似文件一样的对象;

        json:可选参数;用于在HTTP Request请求体中发送的可json序列化的Python对象。

        headers:可选参数;用于在HTTP Request请求头中发送的字典。

        cookies:可选参数;HTTP Request请求头中发送的字典或CookieJar对象。

        files:可选参数;在请求中发送一个文件,该参数可以是如下格式:'name': file-like-objects`格式的字典,或者{'name': file-tuple}用于多部分编码文件上传。其中file-tuple可以是2元素元祖('filename', fileobj),三元素元祖('filename', fileobj, 'content_type'),四元素元祖('filename', fileobj, 'content_type', custom_headers)。元祖中content_type是string类型,用于指定给定文件的内容类型;元祖中custom_headers是为文件额外加的类似于字典对象一样的请求头。

        auth:可选参数;元祖类型,用于启用Basic/Digest/Custom(基本,数字签名,自定义)等http身份认证。

        timeout:可选参数;在放弃请求之前等待服务器发送数据的时间,单位秒(s),可以是一个浮点型,也可以是一个元祖。若为元祖,元祖可为(connect timeout, read timeout),其中connect timeout连接超时是指发送请求的地址开始到连接上目标url主机地址的时间,read timeout读取时间是指是Http客户端已经连接到了目标服务器,然后进行内容数据的获取的时间。

        allow_redirects:可选参数;布尔型,是否运行重定向,默认为True。

        proxies:可选参数;字典类型,代理url。

        verify:可选参数;可以是布尔型,它控制我们是否验证服务器的TLS证书;或者是一个字符串,是一个CA bundle的路径地址。默认值为True。

        stream:可选参数;当为False时,响应内容将会被立即下载。

        cert:可选参数;如果是字符串,是ssl客户端证书文件(.pem)路径;如果是一个元祖,则为证书,证书加密key,('cert', 'key') pair。

    函数返回:Response对象,类型是:requests.Response

1.2 requests.get请求方法

def get(url, params=None, **kwargs):
    r"""Sends a GET request.

    :param url: URL for the new :class:`Request` object.
    :param params: (optional) Dictionary, list of tuples or bytes to send
        in the query string for the :class:`Request`.
    :param \*\*kwargs: Optional arguments that ``request`` takes.
    :return: :class:`Response <Response>` object
    :rtype: requests.Response
    """

    kwargs.setdefault('allow_redirects', True)
    return request('get', url, params=params, **kwargs)

    参数解释和函数返回同request方法。

1.3 requests.post请求方法

def post(url, data=None, json=None, **kwargs):
    r"""Sends a POST request.

    :param url: URL for the new :class:`Request` object.
    :param data: (optional) Dictionary, list of tuples, bytes, or file-like
        object to send in the body of the :class:`Request`.
    :param json: (optional) json data to send in the body of the :class:`Request`.
    :param \*\*kwargs: Optional arguments that ``request`` takes.
    :return: :class:`Response <Response>` object
    :rtype: requests.Response
    """

    return request('post', url, data=data, json=json, **kwargs)

    参数解释和函数返回同request方法。

2、响应对象Response

class Response(object):
    """The :class:`Response <Response>` object, which contains a
    server's response to an HTTP request.
    """

    __attrs__ = [
        '_content', 'status_code', 'headers', 'url', 'history',
        'encoding', 'reason', 'cookies', 'elapsed', 'request'
    ]

    def __init__(self):
        self._content = False
        self._content_consumed = False
        self._next = None

        #: Integer Code of responded HTTP Status, e.g. 404 or 200.
        self.status_code = None

        #: Case-insensitive Dictionary of Response Headers.
        #: For example, ``headers['content-encoding']`` will return the
        #: value of a ``'Content-Encoding'`` response header.
        self.headers = CaseInsensitiveDict()

        #: File-like object representation of response (for advanced usage).
        #: Use of ``raw`` requires that ``stream=True`` be set on the request.
        #: This requirement does not apply for use internally to Requests.
        self.raw = None

        #: Final URL location of Response.
        self.url = None

        #: Encoding to decode with when accessing r.text.
        self.encoding = None

        #: A list of :class:`Response <Response>` objects from
        #: the history of the Request. Any redirect responses will end
        #: up here. The list is sorted from the oldest to the most recent request.
        self.history = []

        #: Textual reason of responded HTTP Status, e.g. "Not Found" or "OK".
        self.reason = None

        #: A CookieJar of Cookies the server sent back.
        self.cookies = cookiejar_from_dict({})

        #: The amount of time elapsed between sending the request
        #: and the arrival of the response (as a timedelta).
        #: This property specifically measures the time taken between sending
        #: the first byte of the request and finishing parsing the headers. It
        #: is therefore unaffected by consuming the response content or the
        #: value of the ``stream`` keyword argument.
        self.elapsed = datetime.timedelta(0)

        #: The :class:`PreparedRequest <PreparedRequest>` object to which this
        #: is a response.
        self.request = None

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.close()

    def __getstate__(self):
        # Consume everything; accessing the content attribute makes
        # sure the content has been fully read.
        if not self._content_consumed:
            self.content

        return {attr: getattr(self, attr, None) for attr in self.__attrs__}

    def __setstate__(self, state):
        for name, value in state.items():
            setattr(self, name, value)

        # pickled objects do not have .raw
        setattr(self, '_content_consumed', True)
        setattr(self, 'raw', None)

    def __repr__(self):
        return '<Response [%s]>' % (self.status_code)

    def __bool__(self):
        """Returns True if :attr:`status_code` is less than 400.

        This attribute checks if the status code of the response is between
        400 and 600 to see if there was a client error or a server error. If
        the status code, is between 200 and 400, this will return True. This
        is **not** a check to see if the response code is ``200 OK``.
        """
        return self.ok

    def __nonzero__(self):
        """Returns True if :attr:`status_code` is less than 400.

        This attribute checks if the status code of the response is between
        400 and 600 to see if there was a client error or a server error. If
        the status code, is between 200 and 400, this will return True. This
        is **not** a check to see if the response code is ``200 OK``.
        """
        return self.ok

    def __iter__(self):
        """Allows you to use a response as an iterator."""
        return self.iter_content(128)

    @property
    def ok(self):
        """Returns True if :attr:`status_code` is less than 400, False if not.

        This attribute checks if the status code of the response is between
        400 and 600 to see if there was a client error or a server error. If
        the status code is between 200 and 400, this will return True. This
        is **not** a check to see if the response code is ``200 OK``.
        """
        try:
            self.raise_for_status()
        except HTTPError:
            return False
        return True

    @property
    def is_redirect(self):
        """True if this Response is a well-formed HTTP redirect that could have
        been processed automatically (by :meth:`Session.resolve_redirects`).
        """
        return ('location' in self.headers and self.status_code in REDIRECT_STATI)

    @property
    def is_permanent_redirect(self):
        """True if this Response one of the permanent versions of redirect."""
        return ('location' in self.headers and self.status_code in (codes.moved_permanently, codes.permanent_redirect))

    @property
    def next(self):
        """Returns a PreparedRequest for the next request in a redirect chain, if there is one."""
        return self._next

    @property
    def apparent_encoding(self):
        """The apparent encoding, provided by the chardet library."""
        return chardet.detect(self.content)['encoding']

    def iter_content(self, chunk_size=1, decode_unicode=False):
        """Iterates over the response data.  When stream=True is set on the
        request, this avoids reading the content at once into memory for
        large responses.  The chunk size is the number of bytes it should
        read into memory.  This is not necessarily the length of each item
        returned as decoding can take place.

        chunk_size must be of type int or None. A value of None will
        function differently depending on the value of `stream`.
        stream=True will read data as it arrives in whatever size the
        chunks are received. If stream=False, data is returned as
        a single chunk.

        If decode_unicode is True, content will be decoded using the best
        available encoding based on the response.
        """

        def generate():
            # Special case for urllib3.
            if hasattr(self.raw, 'stream'):
                try:
                    for chunk in self.raw.stream(chunk_size, decode_content=True):
                        yield chunk
                except ProtocolError as e:
                    raise ChunkedEncodingError(e)
                except DecodeError as e:
                    raise ContentDecodingError(e)
                except ReadTimeoutError as e:
                    raise ConnectionError(e)
            else:
                # Standard file-like object.
                while True:
                    chunk = self.raw.read(chunk_size)
                    if not chunk:
                        break
                    yield chunk

            self._content_consumed = True

        if self._content_consumed and isinstance(self._content, bool):
            raise StreamConsumedError()
        elif chunk_size is not None and not isinstance(chunk_size, int):
            raise TypeError("chunk_size must be an int, it is instead a %s." % type(chunk_size))
        # simulate reading small chunks of the content
        reused_chunks = iter_slices(self._content, chunk_size)

        stream_chunks = generate()

        chunks = reused_chunks if self._content_consumed else stream_chunks

        if decode_unicode:
            chunks = stream_decode_response_unicode(chunks, self)

        return chunks

    def iter_lines(self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=False, delimiter=None):
        """Iterates over the response data, one line at a time.  When
        stream=True is set on the request, this avoids reading the
        content at once into memory for large responses.

        .. note:: This method is not reentrant safe.
        """

        pending = None

        for chunk in self.iter_content(chunk_size=chunk_size, decode_unicode=decode_unicode):

            if pending is not None:
                chunk = pending + chunk

            if delimiter:
                lines = chunk.split(delimiter)
            else:
                lines = chunk.splitlines()

            if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]:
                pending = lines.pop()
            else:
                pending = None

            for line in lines:
                yield line

        if pending is not None:
            yield pending

    @property
    def content(self):
        """Content of the response, in bytes."""

        if self._content is False:
            # Read the contents.
            if self._content_consumed:
                raise RuntimeError(
                    'The content for this response was already consumed')

            if self.status_code == 0 or self.raw is None:
                self._content = None
            else:
                self._content = b''.join(self.iter_content(CONTENT_CHUNK_SIZE)) or b''

        self._content_consumed = True
        # don't need to release the connection; that's been handled by urllib3
        # since we exhausted the data.
        return self._content

    @property
    def text(self):
        """Content of the response, in unicode.

        If Response.encoding is None, encoding will be guessed using
        ``chardet``.

        The encoding of the response content is determined based solely on HTTP
        headers, following RFC 2616 to the letter. If you can take advantage of
        non-HTTP knowledge to make a better guess at the encoding, you should
        set ``r.encoding`` appropriately before accessing this property.
        """

        # Try charset from content-type
        content = None
        encoding = self.encoding

        if not self.content:
            return str('')

        # Fallback to auto-detected encoding.
        if self.encoding is None:
            encoding = self.apparent_encoding

        # Decode unicode from given encoding.
        try:
            content = str(self.content, encoding, errors='replace')
        except (LookupError, TypeError):
            # A LookupError is raised if the encoding was not found which could
            # indicate a misspelling or similar mistake.
            #
            # A TypeError can be raised if encoding is None
            #
            # So we try blindly encoding.
            content = str(self.content, errors='replace')

        return content

    def json(self, **kwargs):
        r"""Returns the json-encoded content of a response, if any.

        :param \*\*kwargs: Optional arguments that ``json.loads`` takes.
        :raises ValueError: If the response body does not contain valid json.
        """

        if not self.encoding and self.content and len(self.content) > 3:
            # No encoding set. JSON RFC 4627 section 3 states we should expect
            # UTF-8, -16 or -32. Detect which one to use; If the detection or
            # decoding fails, fall back to `self.text` (using chardet to make
            # a best guess).
            encoding = guess_json_utf(self.content)
            if encoding is not None:
                try:
                    return complexjson.loads(
                        self.content.decode(encoding), **kwargs
                    )
                except UnicodeDecodeError:
                    # Wrong UTF codec detected; usually because it's not UTF-8
                    # but some other 8-bit codec.  This is an RFC violation,
                    # and the server didn't bother to tell us what codec *was*
                    # used.
                    pass
        return complexjson.loads(self.text, **kwargs)

    @property
    def links(self):
        """Returns the parsed header links of the response, if any."""

        header = self.headers.get('link')

        # l = MultiDict()
        l = {}

        if header:
            links = parse_header_links(header)

            for link in links:
                key = link.get('rel') or link.get('url')
                l[key] = link

        return l

    def raise_for_status(self):
        """Raises :class:`HTTPError`, if one occurred."""

        http_error_msg = ''
        if isinstance(self.reason, bytes):
            # We attempt to decode utf-8 first because some servers
            # choose to localize their reason strings. If the string
            # isn't utf-8, we fall back to iso-8859-1 for all other
            # encodings. (See PR #3538)
            try:
                reason = self.reason.decode('utf-8')
            except UnicodeDecodeError:
                reason = self.reason.decode('iso-8859-1')
        else:
            reason = self.reason

        if 400 <= self.status_code < 500:
            http_error_msg = u'%s Client Error: %s for url: %s' % (self.status_code, reason, self.url)

        elif 500 <= self.status_code < 600:
            http_error_msg = u'%s Server Error: %s for url: %s' % (self.status_code, reason, self.url)

        if http_error_msg:
            raise HTTPError(http_error_msg, response=self)

    def close(self):
        """Releases the connection back to the pool. Once this method has been
        called the underlying ``raw`` object must not be accessed again.

        *Note: Should not normally need to be called explicitly.*
        """
        if not self._content_consumed:
            self.raw.close()

        release_conn = getattr(self.raw, 'release_conn', None)
        if release_conn is not None:
            release_conn()

    返回释义:

        Response.url:响应的最终url。

        Response.headers:响应头信息。

        Response.cookies:服务器返回的cookies的CookieJar对象。

        Response.content:响应内容,字节类型(bytes)。

        Response.text:响应内容,Unicode类型。

        Response.raw:响应内容,类文件对象(File-like object)。

        Response.json:响应内容,json格式;函数定义def json(self, **kwargs),可选参数的关键字参数可用json.load的参数。

        Response.links:返回响应的解析头链接(如果有的话)。

        Response.raise_for_status:HTTP响应状态码。

        Response.elapsed:从发送请求的第一个字节到解析响应头的时间差,不受stream参数和响应内容的影响。

        Response.encoding:在访问Response.text对象时,解码使用的编码。

        Response.history:Request历史记录中的:class: ' Response <Response> '对象列表任何重定向响应都将在这里结束。该列表从最早的请求到最近的请求进行排序。

        Response.request:该响应对应的请求对象。

3、发送request请求并获取响应示例(requests+pytest)

3.1 get请求

#-*- coding:utf-8 -*-

import requests
import json

def test_get():
    r = requests.get(url="http://www.baidu.com")

    print("最终请求url:%s" % r.url)
    print("响应头为:%s" %r.headers)
    print("响应内容:%s" % r.text)
    print("响应的HTTP状态码:%s" % r.status_code)
    print("响应解码的编码格式:%s" % r.encoding)
    print("响应时间(时:分:秒:毫秒):%s" % r.elapsed)
    print("响应时间(s):%s " % r.elapsed.total_seconds())
    print("响应对应的请求头:%s" % r.request.headers)

代码运行结果:

最终请求url:http://www.baidu.com/

响应头为:{'Cache-Control': 'private, no-cache, no-store, proxy-revalidate, no-transform', 'Content-Encoding': 'gzip', 'Content-Type': 'text/html', 'Date': 'Thu, 10 Jun 2021 06:25:50 GMT', 'Last-Modified': 'Mon, 23 Jan 2017 13:27:36 GMT', 'Pragma': 'no-cache', 'Server': 'bfe/1.0.8.18', 'Set-Cookie': 'BDORZ=27315; max-age=86400; domain=.baidu.com; path=/', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive'}

响应内容:<!DOCTYPE html>

<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知é“</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn"></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>æ–°é—»</a> <a href=http://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>è´´å§</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>');</script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产å“</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>å
³äºŽç™¾åº¦</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使用百度å‰å¿
读</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>æ„è§å馈</a>&nbsp;京ICPè¯030173å·&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>

 

响应的HTTP状态码:200

响应解码的编码格式:ISO-8859-1

响应时间(时:分:秒:毫秒):0:00:00.039571

响应时间(s):0.039571

响应对应的请求头:{'User-Agent': 'python-requests/2.25.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}

3.2 post请求

#-*- coding:utf-8 -*-

import requests
import json

def test_post():
    url = "http://127.0.0.1:9091/api/v1/user/login"
    data = json.dumps({"authRequest": {"userName": "user01", "password": "pwd"}})
    headers = {"Content-Type": "application/json"}
    r = requests.post(url=url, data=data)

    print("最终请求url:%s" % r.url)
    print("响应头为:%s" % r.headers)
    print("响应内容:%s" % r.text)
    print("响应的HTTP状态码:%s" % r.status_code)
    print("响应解码的编码格式:%s" % r.encoding)
    print("响应时间(时:分:秒:毫秒):%s" % r.elapsed)
    print("响应时间(s):%s " % r.elapsed.total_seconds())
    print("响应对应的请求头:%s" % r.request.headers)

代码运行结果:

最终请求url:http://127.0.0.1:9091/api/v1/user/login
响应头为:{'Content-Type': 'application/json', 'Content-Length': '138', 'Server': 'Werkzeug/1.0.1 Python/3.8.5', 'Date': 'Thu, 10 Jun 2021 06:57:29 GMT', 'Connection': 'close'}
响应内容:{
  "access_token": "3b6754f00bb0063071c5b71ce2b56b4ed0ce56a63493e785bea85b74c41ce200", 
  "code": "200", 
  "message": "login success"
}

响应的HTTP状态码:200
响应解码的编码格式:utf-8
响应时间(时:分:秒:毫秒):0:00:00.018574
响应时间(s):0.018574 
响应对应的请求头:{'User-Agent': 'python-requests/2.25.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '58'}

三、断言

1、普通断言

示例:

#-*- coding:utf-8 -*-

import json
import pytest
import requests
def test_get():
    r = requests.get(url="http://www.baidu.com")
    http_code = r.status_code
    assert http_code == 200 #普通断言

运行结果:

test_requests.py .                                                       [100%]

============================== 1 passed in 0.17s ===============================

Process finished with exit code 0

2、jsonpath断言

    jsonpath是对json序列的解析,利用jsonpath,我们可以获取json序列中想要的key对应的value。

    jsonpath参考文档:https://goessner.net/articles/JsonPath/

    jsonpath安装:pip3 install jsonpath

    示例:

#-*- coding:utf-8 -*-

from jsonpath import jsonpath

json_str = { "store": {
    "book": [
      { "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      { "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      { "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      { "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}

def test_json_path():
    books_gt_20 = jsonpath(json_str, "$..book[?(@.price > 20)]")
    print("\n")
    print("books_gt_20: %s" %books_gt_20)
    assert len(books_gt_20) == 1

运行结果:

test_jsonpath.py .

books_gt_20: [{'category': 'fiction', 'author': 'J. R. R. Tolkien', 'title': 'The Lord of the Rings', 'isbn': '0-395-19395-8', 'price': 22.99}]
                                                       [100%]

============================== 1 passed in 0.02s ===============================

Process finished with exit code 0

3、json schema断言

    JSON Schema是一个允许您注释和验证JSON文档的词汇表。它可以验证json中某一个key对应的value是否是某种类型,是否满足一定的范围等。

    json schema官网:https://json-schema.org/

    json schema版本参考:http://json-schema.org/implementations.html

    json schema生成器:https://jsonschema.net/

    jsonschema gitbub地址:https://github.com/Julian/jsonschema

    jsonschema安装:pip3 install jsonschema

    示例:

#-*- coding:utf-8 -*-

import json
from jsonschema import validate

#原json数据
json_data = {"name": "name", "password": 123}

#json schema数据
json_schema = {
    "type": "object",
    "default": {},
    "required": [
        "name",
        "password"
    ],
    "properties": {
        "name": {
            "$id": "#/properties/name",
            "type": "string",
            "title": "The Checked Schema",
            "description": "An explanation about the purpose of this instance.",
            "default": None,
            "examples": [
                "name"
            ]
        },
        "password": {
            "$id": "#/properties/password",
            "type": "integer",
            "title": "The Dimensions Schema",
            "description": "An explanation about the purpose of this instance.",
            "default": 0,
            "examples": [
                123
            ],

        }
    }
}

def test_jsonSchema():
    json_schema = json.load(open('jsonSchema.json', 'r'))
    validate(json_data, schema=json_schema)

    运行结果:

test_jsonSchema.py .                                                     [100%]

============================== 1 passed in 0.08s ===============================