首页 > 编程语言 > 详细

Python实用库使用与浅析系列一:httmock

时间:2020-11-18 19:37:13      阅读:72      评论:0      收藏:0      [点我收藏+]

 

介绍

这个系列的第一篇文章,介绍一下httmook库使用和原理,代码只有200多行,实现的很巧妙。

应用场景:有时会需要调用外部接口,拿到返回数据用以满足当前的测试任务的需求。但是当外部接口不可用,或者没有提供测试用环境时,就需要mock接口。

pypi链接:https://pypi.org/project/httmock/

安装:pip install httmock

使用

httpmook提供两个装饰器接口:

  • urlmatch
  • all_requests

urlmatch拦截匹配url的请求

from httmock import urlmatch, HTTMock
import requests

@urlmatch(netloc=r(.*\.)?google\.com$)
def google_mock(url, request):
    return Feeling lucky, punk?

with HTTMock(google_mock):
    r = requests.get(http://google.com/)
print r.content  # ‘Feeling lucky, punk?‘

all_requests拦截所有请求:

from httmock import all_requests, HTTMock
import requests

@all_requests
def response_content(url, request):
    return {status_code: 200,
            content: Oh hai}

with HTTMock(response_content):
    r = requests.get(https://foo_bar)

print r.status_code
print r.content

如何构造返回数据:

from httmock import all_requests, response, HTTMock
import requests

@all_requests
def response_content(url, request):
    headers = {content-type: application/json,
               Set-Cookie: foo=bar;}
    content = {message: API rate limit exceeded}
    return response(403, content, headers, None, 5, request)

with HTTMock(response_content):
    r = requests.get(https://api.github.com/users/whatever)

print r.json().get(message)
print r.cookies[foo]

原理

我以第一个示例来说明:

通过with语实例一个上下文解析器的类的实例,并传入带有装饰器(urlmatch)的返回数据构造函数google_mock,这是我们通过下面几行代码能看到的:

from httmock import urlmatch, HTTMock
import requests

@urlmatch(netloc=r(.*\.)?google\.com$)
def google_mock(url, request):
    return Feeling lucky, punk?

with HTTMock(google_mock):
    r = requests.get(http://google.com/)
print r.content  # ‘Feeling lucky, punk?‘

HTTMook的初始化函数,初始化被装饰的函数google_mock

    def __init__(self, *handlers):
        self.handlers = handlers

上下文解析器的__enter__做了什么:

    def __enter__(self):
        self._real_session_send = requests.Session.send
        self._real_session_prepare_request = requests.Session.prepare_request

        for handler in self.handlers:
            handler_clean_call(handler)

        #制造假的send函数
        def _fake_send(session, request, **kwargs):
            response = self.intercept(request, **kwargs)
            if isinstance(response, requests.Response):
                # this is pasted from requests to handle redirects properly:
                kwargs.setdefault(stream, session.stream)
                kwargs.setdefault(verify, session.verify)
                kwargs.setdefault(cert, session.cert)
                kwargs.setdefault(proxies, session.proxies)

                allow_redirects = kwargs.pop(allow_redirects, True)
                stream = kwargs.get(stream)
                timeout = kwargs.get(timeout)
                verify = kwargs.get(verify)
                cert = kwargs.get(cert)
                proxies = kwargs.get(proxies)

                gen = session.resolve_redirects(
                    response,
                    request,
                    stream=stream,
                    timeout=timeout,
                    verify=verify,
                    cert=cert,
                    proxies=proxies)

                history = [resp for resp in gen] if allow_redirects else []

                if history:
                    history.insert(0, response)
                    response = history.pop()
                    response.history = tuple(history)

                session.cookies.update(response.cookies)

                return response

            return self._real_session_send(session, request, **kwargs)

        def _fake_prepare_request(session, request):
            """
            Fake this method so the `PreparedRequest` objects contains
            an attribute `original` of the original request.
            """
            prep = self._real_session_prepare_request(session, request)
            prep.original = request
            return prep

        #替换requests的send与prepare_request函数
        requests.Session.send = _fake_send
        requests.Session.prepare_request = _fake_prepare_request

        return self

1、首先保存了requests.Session.send与requests.Session.prepare_request

2、handler_clean_call(handler),对handlers做了预处理

3、制造替换函数,_fake_send与_fake_prepare_request,并替换requests中原始的函数,作为一门动态语言的优势现在体现出来了:

        #替换requests的send与prepare_request函数
        requests.Session.send = _fake_send
        requests.Session.prepare_request = _fake_prepare_request

_fake_send函数最重要的代码:

response = self.intercept(request, **kwargs)

intercept函数的作用:执行handel函数,拿到构造的返回数据:

 def intercept(self, request, **kwargs):
        url = urlparse.urlsplit(request.url)
        res = first_of(self.handlers, url, request)if isinstance(res, requests.Response):return res
        elif isinstance(res, dict):return response(res.get(status_code),
                            res.get(content),
                            res.get(headers),
                            res.get(reason),
                            res.get(elapsed, 0),
                            request,
                            stream=kwargs.get(stream, False),
                            http_vsn=res.get(http_vsn, 11))
        elif isinstance(res, (text_type, binary_type)):return response(content=res, stream=kwargs.get(stream, False))
        elif res is None:return None
        else:raise TypeError(
                "Dont know how to handle response of type {0}".format(type(res)))

最后执行handle的函数:

也可以看到google_mock(url, request)的两个参数是如何传入的,这是由于_fake_send替换掉requests运行时态的send包,在执行过程中_fake_send拿到request这个实例封装参数。

def first_of(handlers, *args, **kwargs):
    for handler in handlers:
        res = handler(*args, **kwargs)
        if res is not None:
            return res

上下文解析器的__exit__做了什么:

替换掉运行时的send与prepare函数:

    def __exit__(self, exc_type, exc_val, exc_tb):
        #恢复
        requests.Session.send = self._real_session_send
        requests.Session.prepare_request = self._real_session_prepare_request

 

Python实用库使用与浅析系列一:httmock

原文:https://www.cnblogs.com/-wenli/p/14001207.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!