python代码学习——接口自动化一(接口基础与requests)
接口基础知识
- 接口在广义上可以分为内部接口和外部接口,内部接口指的是公司内部的接口,例如登录;外部接口指的是调用第三方的接口,例如获取验证码、支付等等
- 按照分类,可以分为硬件接口和软件接口,软件接口又分为程序内部接口和外部接口
-
-
- 软件接口:简单的来说,就是软件程序之间的数据交互通道
-
-
- 外部接口:是跨系统平台与平台之间的对接,例如微信、支付宝的支付接口等
- 按照不同的请求协议,分为http、webservice、dubbo、socket,如何得知公司产品的协议?答案:问开发
-
- 进行接口测试的工具:postman,jmeter(http)、soapui(webservice)
- 接口的本质:调用类里面的方法
- http协议请求分为get、post、delete、head、optioon
- webservice:经过封装的post请求
- 什么是接口功能测试?——本质上是基于某种协议,模拟客户端发送请求给服务器,服务器返回响应数据,然后对响应数据进行分析,判断和我们的预期是否一致,从而验证功能是否正确
为什么要做接口测试
![在这里插入图片描述](https://img-blog.csdnimg.cn/a235f9d1c171433ba68f21ab3a0997bf.png)
如果要进行接口测试,没有接口文档的话要怎么做
- 首先,可以考虑推送开发去完善接口文档
- 或者:自力更生,抓包,F12查看,咨询开发
协议和HTTP请求
- 不论是哪一种接口,本质上都是通过某一种传输协议(例如http、https、webservice),在客户端和服务器之间传输数据
- http协议是目前最为广泛的协议,https是在http的基础上做了一层加密,会更加安全
- webservice协议的接口,一般是使用soap协议通过http进行传输,请求的报文和相应的报文一般是xml格式
Http协议的细则
- 一个http请求包括:head(请求头)信息、响应信息、cookies、html、缓存
- 请求头:保存请求的一些配置信息,例如请求的长度,content_type,cookies等
- 请求头信息并不是每一次请求都要,但是如果请求中必须要请求头(headers),那么必须要加
- 请求头跟随requesr中的hearers参数,以字典/json的形式发送
- url:请求地址
- get/post: 请求方法
- 响应头:服务器返回回来的数据,一般是json
- Request Payload:请求数据,一般传递的数据是json
http常见的状态码
- 200 :正常,一切正常,服务器接收请求也作出了正确的处理
- 4XX:客户端错误:
-
-
- 403:禁止 服务器理解客户请求,但是拒绝处理,通常由服务器上的文件或目录的权限导致
- 3XX:表示重定向相关
-
- 302:临时重定向:请求地址的文档被移动或者删除,文档新的url在location响应头中给出
-
-
- 304:未修改:表示客户机器缓存的版本是最新的,应该继续使用他(具体说直接取缓存的数据),比如说前端js,主要指的是静态资源
- 5XX:表示服务器的错误
-
-
-
get请求和post请求的区别
- 传参位置不同:
-
- get请求的参数拼接在接口地址后面,通过?进行拼接,多个参数之间会使用&连接,其是查询参数
-
- 传递参数的长度有限制
-
-
- post请求理论上没有长度限制,如果要传递大量数据的话,一般会使用post的方式
- 安全性也有区别
-
- get请求因为参数直接写在地址后面,所以相对而言报参数容易暴漏
-
cookies与session
- cookies:在客户端村存储用户的一些数据,例如用户名或浏览器记录
- seeeion:在服务器及用户的请求状态,一般默认时间是30min,超时重新登录,有session决定。一般以session_id的形式呈现出来
- 机制:session_id会记录在cookies中,每次请求,cookies的所有信息都会传给服务器,服务器通过session_id来识别是否是同一个用户的请求,不是一个用户的话,会要求用户重新登录
- 这种机制的原因:http请求是无状态的、无标记的
- session存在过期,cookies没有过期
访问授权与鉴权
- 鉴权:访问的接口是否正常,是否非法访问,越过前端访问,token
- 授权:是否只有访问接口的权限 ,常出现在加密的或者收费的第三方接口,并不是任何人都可以访问的,如果要正常访问,必须要传key,一般来说是唯一的、全局的、动态的,具有一定的特征
- 验签,常出现在app或SDK的应用中,参靠以下网址:https://blog.csdn.net/sjy8207380/article/details/79232644
- jemeter和soapUI的使用方法:
如何用python做接口测试
- 利用python去发起http请求
- 可利用urlib、urlib2、requests模块都可实现利用python发起http请求
- 在此只讲解requests模块
- 首先安装requests第三方库
- 导入requests模块
- 如果遇到接口不会处理怎么办?http://docs.python-requests.org/zh_CN/latest/
Requests模块
- 通过代码发起的请求与通过网站/app/接口测试工具发起的请求是一样的,都是从客户端发起
- requests 的简单使用样例
import requests
url = 'https://xgsj.istarshine.com/login'
data = requests.get(url)
print(data)
Get请求
- requests中的get模块,就是一个函数
- 存在return的返回值,返回的是一个对象,可用变量来接受存储这个值,打印结果如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/21fd9c9016fa428d964926dda3f6ae4e.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzc1NDg3OQ==,size_16,color_FFFFFF,t_70)
- get中存在三个参数,url、param和**kwargs;其中param存在默认参数None
- url指的是数据地址,param指的是数据发送的格式,**kwargs指的是请求的时候带的参数
- get返回的是一个request请求函数
- 以下是get函数的的代码详情
![在这里插入图片描述](https://img-blog.csdnimg.cn/800b2223dec441c4ba67f878ff4fa88e.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzc1NDg3OQ==,size_16,color_FFFFFF,t_70)
request 请求函数
- request 中存在method、url和 **kwargs(关键字参数,不传入也可以)三个参数
- 其中method指的是请求方式,url指的是请求地址,**kwargs是关键字参数默契意味着参数会以键值对的形式传入
- 其中的关键字参数必须传requeat指定的那些参数,例如:method、url、data、json、params、headers、cookies、files、proxies(代理)、auth、time-out、verify、class、request等等
- requeats支持的请求方式有许多种,`GET
,
OPTIONS,
HEAD,
POST,
PUT,
PATCH, 和 DELETE
.都是http请求的method,但是常用的是get和post请求
![在这里插入图片描述](https://img-blog.csdnimg.cn/2e72ba1128ca4599a3e6da867244a55d.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzc1NDg3OQ==,size_16,color_FFFFFF,t_70)
post请求
- 如果一个url,使用get请求返回200,使用post请求返回404,说明url不支持post请求
- post请求中,根据headers中的content-type不同,可以将其传参形式分为三种,data,json和file
- post请求中存在url、data、json和关键字参数
- url指的是请求地址;data和json是默认参数,含带默认值None;
- post请求中,根据headers中的content-type不同,可以将其传参形式分为三种,data,json和file
- headers可以不传,默认为None
- 存在return返回值,返回值是兑现对象
![在这里插入图片描述](https://img-blog.csdnimg.cn/b9e1923f71f44cd5be1afd9747e59551.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzc1NDg3OQ==,size_16,color_FFFFFF,t_70)
json形式传参
- 如果content-type的传参为【application/json】,那么就需要传递json参数,requests中也需要用json接收
import requests
json= {"key1":"value1","key2":"value2"}
res = requests.post('http://www.httpbin.org/post',json=json)
文件形式传参
- 当我们需要传输大容量的数据到服务端时(比如上传文件),我们通常使用content-type=【multipart/form-data】传参类型
- 在requests中通过指定files参数
url = 'http://httpbin.org/post'
files = {
"file": ("test.png", open("test.png", "rb"), "images/png")
}
r = requests.post(url, files=files)
import pathlib
import requests
headers = {"Authorization":"bearere461bdb3-986f-42f8-8100-9545bf9d6c93"}
path_png = pathlib.Path(__file__).absolute().parent /"ApiAndExc"/"data"/"panda.png"
file_data = {"file":("panda.png",open(f"{path_png}","rb"),"images/png")}
res = requests.post("http://shop.lemonban.com:8107/p/file/upload",files=file_data,headers=headers)
print(res.text)
===========================run_result============================
{"resourcesUrl":"http://shop.lemonban.com:8108/","filePath":"2023/11/d4d065a9450e4e4d829e447131f4e4a2.png"}
data传参
- 非json和文件类型的传参方式,基本都是data接收,例如常见的表单传参:content-type= 【application/x-www-form-urlencoded】
import requests
data = {"key1":"value1","key2":"value2"}
res = requests.post('http://www.httpbin.org/post',data=data)
requests模块的return返回
- 不论是get还是post,其请求的结果return返回的都是对象,是个响应实体
- 一个repose包含了响应头、响应正文和状态码和相应时间
查看模块返回信息
- 使用
headers
来查看响应 - 使用
text
来查看正文 - 使用
status_code
来查看code码 - 使用
elapsed.total_seconds()
来查看接口的响应时间
import requests
url = 'http://shop.lemonban.com:8107/login'
login_param = {"principal":"lemon_auto","credentials":"lemon123456","appType":3,"loginType":0}
headers = {"Content-Type":"application/json"}
data_1 = requests.post(url,json=login_param,headers=headers)
print(data_1)
data_headers = data_1.headers
data_code = data_1.status_code
data_request_headers = data_1.request.headers
data_text = data_1.text
data_json= data_1.json()
data_time = data_1.elapsed.total_seconds()
print(data_headers)
int(data_code)
print(data_request_headers)
print(data_text)
print(data_json)
print(data_time)
查看cookies
- 如果接口文档中没有明确的规定说,请求是json格式的,那么请求都是放在字典中的
- cookies,向服务器表名请求是同一个账号发的
- 一般来说,登录成功后会存在cookies
- cookies的获取方式:直接用return返回值.cookies即可;因没有合适的网站,在此不做样例展示
- cookies其实是一种 类字典的形式,可以用字典取值的方式进行取值
import requests
login='http://47.107.168.87:8080/futureloan/mvc/api/member/login'
login_data={'mobilephone':18688773467,'pwd':'123456'}
res = requests.post(url=login,data=login_data)
res_meg = res.text
cookies_mes = res.cookies
print(cookies_mes)
print(res_meg)
cookies的常见问题
- 什么时候带cookies?
-
- 答:如果是cookies+session的验证方式,用户登录成功后一般会返回cookies,接口需要验证登录权限的时候,headers中需要带cookies进行验证
- text和json都能够获取结果,用哪个比较好?
-
- 用户登录成功后带cookies的才能操作的请求,有另外一种方式来解决吗?
-
-
- requests模块中封装了session模块,该模块可以自动管理session的会话信息,自动保存cookies到连接中,这样就不需要主动传递cookies了
![- 使用session的方式请求,可以](https://img-blog.csdnimg.cn/f1c9c65e1ad240af95e3a8a0baa203ec.png)
将request写成一个类,支持两种请求
import requests
def http_request(url,param,mechod,cookie=None):
if mechod=='get':
res=requests.get(url,param,cookies=cookie)
else:
res=requests.post(url,param,cookies=cookie)
return res
if __name__=='__main':
login='http://47.107.168.87:8080/futureloan/mvc/api/member/login'
login_data={'mobilephone':18688773467,'pwd':'123456'}
recharge='http://47.107.168.87:8080/futureloan/mvc/api/member/recharge'
recharge_data={'mobilephone':18688773467,'amount':'1000'}
res_login=http_request(login,login_data,'get')
print('登录的结果是',res_login.json())
print('-'*50)
res_recharge=http_request(recharge,recharge_data,'post',cookie=res_login.cookies)
print('充值的结果是',res_recharge.json())
json和jsonpath
json的定义
- JSON(JavaScript Object Notation, JavaScript 对象表示法) 是一种轻量级的数据交换格式。
- 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
- 简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。
json的语法规则
-
- 数据在键值对中
-
- 多条数据由逗号分隔
-
- 花括号 {} 保存对象:例如
{"name": "tom","age": "26"}
-
- 中括号 [] 保存数组:例如
{"hobby": ["游戏", "音乐", "电影"]}
-
- JSON值的类型:
-
-
-
- 逻辑值(true 或 false)-- 与Python字典有区别
-
-
-
- 重点注意!!! 标准的JSON中不能通过单引号包裹字符串,需要使用双引号
- 例如
{
"name": "rose",
"age": 10,
"height": 180.5,
"isSingle": false,
"address": {
"provinces": "湖南省",
"city": "长沙"
},
"hobby": ["游戏", "音乐", "电影"],
"weight": null
}
jsonpath
运算符 | 说明 |
---|
$ | 根节点 ~ 相当于xpath中/ |
.(单个英文点) | 获取子节点 |
… (两个英文点) | 在子孙节点中进行递归搜索 $…~ 相当于xpath里面// |
* 星号 | 通配符,可以表示任何元素 |
[a,b] | 选择多个子节点 ~ 了解 |
[start:end] | 切片,区间为[start,end),左闭右开 ~ 了解 |
[?(过滤条件)] | 过滤器表达式,过滤条件结果必须是boolean类型,比如可以是比较表达式或者逻辑表达式 |
@ | 过滤器中处理当前节点,通常会和过滤器一起使用 |
import requests
import jsonpath
res = requests.get('http://mall.lemonban.com:8107/search/searchProdPage?
prodName=平板电脑')
result = jsonpath.jsonpath(res.json(),'$.records[*].prodId')
print(result)
{
"data": {
"page": 1,
"size": 50,
"source": "weibo,weixin,forum,insvideo,video,tv,news,app,pingmei,blog",
"time": "1697472000~1697535034",
"sort_of": "",
"match_fields": [
"content",
{
"content": "公司"
},
{
"filters": [
{
"site_domain": " weixin"
},
{
"site_name": "微信"
},
{
"username": "fyk"
}
]
}
],
"must_kw": "境外_1024",
"title": {
"site_domain": "weibo",
"site_name": "微博",
"username": "fyy"
}
}
}
import jsonpath
import json
with open("data.json","r",encoding="utf-8") as f:
data = json.loads(f.read())
js_1 = jsonpath.jsonpath(data,"$.data")
print("js_1",js_1)
js_2 = jsonpath.jsonpath(data,"$..site_name")
print("js_2",js_2)
js_3 = jsonpath.jsonpath(data,"$..filters")
print("js_3",js_3)
js_4 = jsonpath.jsonpath(data,"$..filters[0:2]")
print("js_4",js_4)
js_5 = jsonpath.jsonpath(data,"$.filters[0:2]")
print("js_5",js_5)
js_6 = jsonpath.jsonpath(data,"$.data.match_fields")
print("js_6",js_6)
js_7 = jsonpath.jsonpath(data,"$..filters[0].site_domain")
print("js_7",js_7)
js_8 = jsonpath.jsonpath(data,"$..title[?(@.username=\"fyy\")]")
print("js_8",js_8)