接口自动化-requests
一、requests搭建框架的特点
https://requests.readthedocs.io/en/master/
>>> r = requests.get('https://api.github.com/user', auth=('user', 'pass')) >>> r.status_code 200 >>> r.headers['content-type'] 'application/json; charset=utf8' >>> r.encoding 'utf-8' >>> r.text '{"type":"User"...' >>> r.json() {'private_gists': 419, 'total_private_repos': 77, ...}
- 功能全面:http/https支持全面
- 使用简单:简单易用,不用关心底层细节
- 定制性搞:借助于hook机制完成通用处理
常见http方法请求构造
r = requests.get('https://api.github.com/events')
r = requests.post('https://httpbin.org/post', data = {'key':'value'})
r = requests.put('https://httpbin.org/put', data = {'key':'value'})
r = requests.delete('https://httpbin.org/delete')
r = requests.head('https://httpbin.org/get')
r = requests.options('https://httpbin.org/get')
一个小的接口测试用例
import requests class TestDemo: def test_demo(self): r=requests.get('http://http.xxx.com/get') status=r.status_code assert status == 200
二、接口请求构造
- get query: path、query
- post body:
- form:
- 结构化请求:json,xml,json rpc
- binary (文件上传)
get query实例
payload = {'key1': 'value1', 'key2': 'value2'} r = requests.get('https://httpbin.org/get', params=payload)#get用的是params, 返回结果是在arg里
print(r.url)
#https://httpbin.org/get?key2=value2&key1=value1
print(r.text)
##response
post 请求实例
payload = {'key1': 'value1', 'key2': 'value2'} r = requests.post("https://httpbin.org/post", data=payload)#post是用data=,response在form中显示。 print(r.text) { ... "form": { "key2": "value2", "key1": "value1" }, ... }
文件上传(binary)
#文件上传 def test_file(self): files={'file':open('report.xls','rb')}#文件以rb模式读取 url='' r=requests.post(url,files=files) #把响应的binary data转换成图片(如果是图片的话) For example, to create an image from binary data returned by a request, you can use the following code: >>> from PIL import Image >>> from io import BytesIO >>> i = Image.open(BytesIO(r.content))
headers构造
>>> url = 'https://api.github.com/some/endpoint' >>> headers = {'user-agent': 'my-app/0.0.1'} >>> r = requests.get(url, headers=headers)
cookies
##If a response contains some Cookies, you can quickly access them: >>> url = 'http://example.com/some/cookie/setting/url' >>> r = requests.get(url) >>> r.cookies['example_cookie_name'] 'example_cookie_value' ##To send your own cookies to the server, you can use the cookies parameter: >>> url = 'https://httpbin.org/cookies' >>> cookies = dict(cookies_are='working') >>> r = requests.get(url, cookies=cookies) >>> r.text '{"cookies": {"cookies_are": "working"}}'
#There are times that you may want to send data that is not form-encoded. If you pass in a string instead of a dict, that data will be posted directly. #For example, the GitHub API v3 accepts JSON-Encoded POST/PATCH data: >>> import json >>> url = 'https://api.github.com/some/endpoint' >>> payload = {'some': 'data'} >>> r = requests.post(url, data=json.dumps(payload))#字典转换成string #Instead of encoding the dict yourself, you can also pass it directly using the json parameter (added in version 2.4.2) and it will be encoded automatically: >>> url = 'https://api.github.com/some/endpoint' >>> payload = {'some': 'data'} >>> r = requests.post(url, json=payload) #Note, the json parameter is ignored if either data or files is passed. #Using the json parameter in the request will change the Content-Type in the header to application/json.
xml实例
url = 'https://api.github.com/some/endpoint' xml='' headers = {'Content-Type': 'application/xml'} r=requests.post(url,data=xml,headers=headers).text
复杂数据结构请求:
将数据存入文件,用工具(mustache,freemaker)解析
- 数据保存:将复杂的xml或者json请求体保存文件模板中
- 数据处理:
- 使用mustache,freemaker等工具解析
- 简单的字符串替换
- 使用json xml api进行结构化解析
- 数据生成:输出最终结果
#用两个花括号识别变量,可以对变量进行自动识别和替换
import pystache
j=pystache.render('Hi {{person}}', {"person":"Lucky"})#第一个参数是str,后面是要改变的变量 print(j)
四、响应断言
- 基本信息:r.url, r.status_code, r.headers, r.cookies
- 响应结果:
- r.text=r.encoding+r.content
- r.json()=r.encoding+r.content+content type json
- r.raw.read(10)
- 对应的请求内容:r.request
五、json、xml响应断言
除了可以用字典key value的形式获取json内某个value的值,
参考:https://www.cnblogs.com/k5210202/p/13079738.html
语法
xpath对比示例
XPath | JSONPath | Result |
/store/book/author |
$.store.book[*].author |
the authors of all books in the store |
//author |
$..author |
all authors |
/store/* |
$.store.* |
all things in store, which are some books and a red bicycle. |
/store//price |
$.store..price |
the price of everything in the store. |
//book[3] |
$..book[2] |
the third book |
//book[last()] |
$..book[(@.length-1)] $..book[-1:] |
the last book in order. |
//book[position()<3] |
$..book[0,1] $..book[:2] |
the first two books |
//book[isbn] |
$..book[?(@.isbn)] |
filter all books with isbn number |
//book[price<10] |
$..book[?(@.price<10)] |
filter all books cheapier than 10 |
//* |
$..* |
all Elements in XML document. All members of JSON structure. |
json 断言实例
from jsonpath import jsonpath def test_json(self): r=requests.get("https://ceshiren.com/categories.json") #name=r.json()['category_list']['categories'][0]['name']#字典key value形式 name=jsonpath(r.json(),'$..name')[0]#jsonpath传入的内容必须是json obj print(name) assert name == '开源项目'
xml 断言实例
def test_xml(self): session = XMLSession() r = session.get('https://www.nasa.gov/rss/dyn/lg_image_of_the_day.rss') print(r.xml.links) #XPath is the main supported way to query an element (learn more): print(r.xml.xpath('//item', first=True))#取item的文字可以是r.xml.xpath('//item', first=True).text
#['http://www.nasa.gov/image-feature/from-the-earth-moon-and-beyond', 'http://www.nasa.gov/image-feature/jpl/pia21974/jupiter-s-colorful-cloud-belts', 'http://www.nasa.gov/', 'http://www.nasa.gov/image-feature/portrait-of-the-expedition-54-crew-on-the-space-station', ...]
#<Element 'item' >
rank = country.find('rank').text
country.get('name')
for child in root:
... print child.tag, child.attrib
<?xml version="1.0"?> <data> <country name="Liechtenstein"> <rank>1</rank> <year>2008</year> <gdppc>141100</gdppc> <neighbor name="Austria" direction="E"/> <neighbor name="Switzerland" direction="W"/> </country> <country name="Singapore"> <rank>4</rank> <year>2011</year> <gdppc>59900</gdppc> <neighbor name="Malaysia" direction="N"/> </country> <country name="Panama"> <rank>68</rank> <year>2011</year> <gdppc>13600</gdppc> <neighbor name="Costa Rica" direction="W"/> <neighbor name="Colombia" direction="E"/> </country> </data>
import xml.etree.ElementTree as ET tree = ET.parse('country_data.xml')#xml存在文件 #root = ET.fromstring(country_data_as_string)#直接从string中导入 root = tree.getroot() >>> for child in root: ... print child.tag, child.attrib ... country {'name': 'Liechtenstein'} country {'name': 'Singapore'} country {'name': 'Panama'} Element has some useful methods that help iterate recursively over all the sub-tree below it (its children, their children, and so on). For example, Element.iter(): >>> for neighbor in root.iter('neighbor'): ... print neighbor.attrib ... {'name': 'Austria', 'direction': 'E'} {'name': 'Switzerland', 'direction': 'W'} {'name': 'Malaysia', 'direction': 'N'} {'name': 'Costa Rica', 'direction': 'W'} {'name': 'Colombia', 'direction': 'E'} Element.findall() finds only elements with a tag which are direct children of the current element. Element.find() finds the first child with a particular tag, and Element.text accesses the element’s text content. Element.get() accesses the element’s attributes: >>> for country in root.findall('country'): ... rank = country.find('rank').text ... name = country.get('name') ... print name, rank ... Liechtenstein 1 Singapore 4 Panama 68 ################### This module provides limited support for XPath expressions for locating elements in a tree. The goal is to support a small subset of the abbreviated syntax; a full XPath engine is outside the scope of the module. import xml.etree.ElementTree as ET root = ET.fromstring(countrydata) # Top-level elements,<data></data> 输出是Element data...
root.findall(".") # All 'neighbor' grand-children of 'country' children of the top-level # elements root.findall("./country/neighbor") # Nodes with name='Singapore' that have a 'year' child root.findall(".//year/..[@name='Singapore']") # 'year' nodes that are children of nodes with name='Singapore' root.findall(".//*[@name='Singapore']/year") # All 'neighbor' nodes that are the second child of their parent root.findall(".//neighbor[2]")
六、cookies, header处理
https://blog.csdn.net/whl190412/article/details/90024671?utm_medium=distribute.pc_relevant_download.none-task-blog-baidujs-1.nonecase&depth_1-utm_source=distribute.pc_relevant_download.none-task-blog-baidujs-1.nonecase
(这个网址还介绍了cookie,session,token的区别)
- cookies简介:
cookie 是一个非常具体的东西,指的就是浏览器里面能永久存储的一种数据,仅仅是浏览器实现的一种数据存储功能。
cookie由服务器生成,发送给浏览器,浏览器把cookie以kv形式保存到某个目录下的文本文件内,下一次请求同一网站时会把该cookie发送给服务器。由于cookie是存在客户端上的,所以浏览器加入了一些限制确保cookie不会被恶意使用,同时不会占据太多磁盘空间,所以每个域的cookie数量是有限的。
目前有些 Cookie 是临时的,有些则是持续的。临时的 Cookie 只在浏览器上保存一段规定的时间,一旦超过规定的时间,该 Cookie 就会被系统清除
- 使用场景
在接口测试过程中,很多情况下,需要发送的请求附带cookies,才会得到正常的响应的结果。所以使用python+requests进行接口自动化测试也是同理,需要在构造接口测试用例时加入cookie。
- 传递cookie的两种方式
- 通过请求头信息传递(headers)
- 通过请求的关键字参数cookies传递
headers 传入cookie
def test_cookie(self): url="http://httpbin.testing-studio.com/cookies" head={ 'User-Agent': 'Lucky', "Cookie":"name=lucky"}#Cookie要大写,没有s. headers 里可以传入其他参数进行自定义 r=requests.get(url,headers=head) print(r.request.headers)
{'User-Agent': 'Lucky', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Cookie': 'name=lucky'}
关键字cookies传入
def test_cookie(self): url="http://httpbin.testing-studio.com/cookies" head={ 'User-Agent': 'Lucky', } cookie={ "rank":"1", "name":"Lucky" }#cookies接受的是字典,也可以用dict函数cookie=dict(name="lucky") r=requests.get(url,headers=head,cookies=cookie) print(r.request.headers)
{'User-Agent': 'Lucky', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Cookie': 'name=Lucky; rank=1'}
返回结果自动把字典转换成键值对形式
七、基本认证(http basic)
基本认证(basic access authentication)是允许http用户代理(如:网页浏览器)在请求时,提供用户名和密码的一种方式。
Get /index.html HTTP/1.0
Host:www.google.com
Authorization: Basic xxxxxxxxxxxxxxxxxxxxxxxxxxxx
基本认证实例
- charles抓包,查看此类请求
- 用auth参数传递认证信息
from requests.auth import HTTPBasicAuth def test_auth(self): url="http://httpbin.testing-studio.com/basic-auth/username/lucky" r=requests.get(url,auth=HTTPBasicAuth('username','lucky')) print(r)
遇到的坑:get没有加上,报错JSONDecodeError("Expecting value", s, err.value) from None
接口测试报错,首先要检查请求是否是对的.因此接口测试中,最重要的是检查请求是否正确。
def test_heads(self): r=requests.get('http://httpbin.testing-studio.com/get',headers={"h":"head demo"}) head=r.json()["headers"]["H"] assert head=="head demo"