在Pytest测试实战(九)里面,把接口请求的的请求地址,请求参数这些都存储到了yaml文件中,然后使用Pytest的参数化可以很轻松的实现几行代码就能够覆盖多个测试场景,关于测试场景的维护只需要在yaml文件中维护就可以了。下来需要思考的是在前面的文章体系中所有的请求都是GET的请求方法,但是请求方法除了GET还有POST等请求方法,所以在本文章中,加入对请求方法的处理机制,维护的yaml文件的内容为:
---#查看所有书籍信息"url": "http://localhost:5000/v1/api/books""method": "get""expect": '"name": "Python接口自动化测试实战"'---#创建书籍信息"url": "http://localhost:5000/v1/api/books""method": "post"dict1:"author": "无涯" "done": true"name": "Python测试实战""expect": '"msg": "添加书籍成功", "status": 1002'---#查看id为1的书籍信息"url": "http://localhost:5000/v1/api/book/1""method": "get""expect": '"author": "wuya"'---#修改id为1的书籍信息"url": "http://localhost:5000/v1/api/book/1""method": "put"dict1:"author": "无涯" "done": true"name": "Python测试实战""expect": '"msg": "更新书的信息成功", "status": 1008'---#删除id为1的书籍信息"url": "http://localhost:5000/v1/api/book/1""method": "delete""expect": '"msg": "删除书籍成功", "status": 1009'
在如上的yaml文件中可以看到我们刻意的增加了method的字段,通过它我们就很轻松的知道场景对应的请求方法是什么。
下来需要思考的是对请求方法如何封装,在requests的库中,我们可以对request的方法进行二次封装和改造,首先来看源码部分,具体如下:
def request(method, url, **kwargs):"""Constructs and sends a :class:`Request `. :param method: method for the new :class:`Request` object. :param url: URL for the new :class:`Request` object. :param params: (optional) Dictionary, list of tuples or bytes to send in the body of 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) ` 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 ` object :rtype: requests.Response Usage:: >>> import requests >>> req = requests.request('GET', 'https://httpbin.org/get') """ # 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)
如上的代码是来源于requests库的源码,这部分代码非常的核心,它显示了在requests的请求中关于地址,方法,以及请求头等很多的信息,如json,data,params,verify,cookies的信息。其实在源码中不需要刻意的去关心GET和POST请求方法,因为它都是来自request的方法之上,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 body of the :class:`Request`. :param \*\*kwargs: Optional arguments that ``request`` takes. :return: :class:`Response ` object :rtype: requests.Response """ kwargs.setdefault('allow_redirects', True)return request('get', url, params=params, **kwargs)
可以看到,在GET的方法中调用了request的方法,只不对针对GET请求又做了特殊的处理,那么**kwargs是什么,它可能是headers,也可能是cookie,verify的信息等信息。依据如上的信息,封装后的请求方法代码如下:
#!/usr/bin/python3
#coding:utf-8
#author:无涯
import requestsclass Request:def request(self,url,method='get',**kwargs):if method=='get':return requests.request(url=url,method=method,**kwargs)elif method=='post':return requests.request(url=url,method=method,**kwargs)elif method=='put':return requests.request(url=url,method=method,**kwargs)elif method=='delete':return requests.request(url=url,method=method,**kwargs)def get(self,url,**kwargs):return self.request(url=url,method='get',**kwargs)def post(self,url,**kwargs):return self.request(url=url,method='post',**kwargs)def put(self,url,**kwargs):return self.request(url=url,method='put',**kwargs)def delete(self,url,**kwargs):return self.request(url=url,method='delete',**kwargs)
在前面两个步骤的基础上,下来在测试的代码中增加对请求方法的判断,如果是GET请求就去调用GET的请求方法,其他的也是如此,完整的代码如下:
#!/usr/bin/python3#coding:utf-8import pytestimport yamlimport osimport requestsimport jsonclass Request:def request(self,url,method='get',**kwargs):if method=='get':return requests.request(url=url,method=method,**kwargs)elif method=='post':return requests.request(url=url,method=method,**kwargs)elif method=='put':return requests.request(url=url,method=method,**kwargs)elif method=='delete':return requests.request(url=url,method=method,**kwargs)def get(self,url,**kwargs):return self.request(url=url,method='get',**kwargs)def post(self,url,**kwargs):return self.request(url=url,method='post',**kwargs)def put(self,url,**kwargs):return self.request(url=url,method='put',**kwargs)def delete(self,url,**kwargs):return self.request(url=url,method='delete',**kwargs)def readYaml():with open('config.yaml','r') as f:return list(yaml.safe_load_all(f))@pytest.mark.parametrize('datas',readYaml())def test_books(datas):if datas['method']=='get':
r=Request().get(url=datas['url'])assert datas['expect'] in json.dumps(r.json(),ensure_ascii=False)elif datas['method']=='post':
r=Request().post(url=datas['url'],json=datas['dict1'])assert datas['expect'] in json.dumps(r.json(),ensure_ascii=False)elif datas['method']=='put':
r=Request().put(url=datas['url'],json=datas['dict1'])assert datas['expect'] in json.dumps(r.json(), ensure_ascii=False)elif datas['method']=='delete':
r=Request().delete(url=datas['url'])assert datas['expect'] in json.dumps(r.json(), ensure_ascii=False)if __name__ == '__main__':
pytest.main(["-v","-s","wxPytest.py"])
执行如上的代码,输出的结果信息如下:
谢谢您的阅读和关注,后续会持续更新。您也可以购买本人出版的书籍和视频实战课程,谢谢!