接口自动化-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
"args": {
'key1': 'value1',
'key2': 'value2'
},

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"}}'

 

三、结构化请求体构造json xml
大部分接口请求和响应都是以结构化的形式展现的,主要是json
json实例
#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实例

xml不常用,data=xml,requests里没有封装xml,所以不能像json那样直接写入,需要在headers里要追加application/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

def test_heads(self):

r=requests.get('http://httpbin.com/get',headers={"h":"head demo"})

head=r.json()["headers"]["H"]

assert head=="head demo"

五、json、xml响应断言

除了可以用字典key value的形式获取json内某个value的值,

也可以用json path,类似于xpath 去写,这样可以处理更加复杂的json结构体,jsonpath要导入

参考: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 断言实例

因为requests没有对xml的封装,可以使用第三方库,requests_xml 就可以实现像json那样的导入了
https://www.ctolib.com/erinxocon-requests-xml.html
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' >
xml语言可以用xml.etree.ElementTree来解析
find是找子节点  
rank = country.find('rank').text
get
country.get('name')
 
attrib
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"

 

 

 


posted @ 2020-12-07 08:23  最最爱学习  阅读(240)  评论(0)    收藏  举报