接口自动化之request模块
一、既然我们有postman\jmeter这些的接口测试工具,为什么要做接口做自动化?
①原因
1.敏捷开发,接口一般数量很大,团队实现接口测试,版本控制。
2.功能太死板,有些接口完全无法实现(复杂的加密接口,签名接口等)
3.接口项目当中有多种不同协议的接口。
4.排错,定位接口问题不方便,结合抓包实现。
5.没有办法生成美观的报告。
6.多接口串联,数据库验证,日志监控,
7.有些公司做web自动化+接口自动化。
②目前市场行情
1.主流测接口工具(中小型项目或公司)jmeter、postman、apifox
2.接口自动化测试(大中型项目或公司)python+requests+pytest+allure+logging+jenkins
3.基于平台的接口自动化(超大型的公司项目)Diango+vue java+mvc
二、回顾HTTP协议
请求:
请求行(请求方式,请求路径,协议)
请求头(常用)
Content-Type:客户端发送的数据格式
applcaton/x-www-form-urlencoded 表单
applcation/json json数据
multipart/form-data 文件上传
application/octrent-stream 二进制文件上传
Accept:接收的数据格式
X-Requested-with 异步请求
User-Agent:客户端类型
请求正文;请求数据
响应:
响应行,(状态码、状态信息)
响应头,
响应正文:响应数据
三.requests请求命令
requests是第三方库,主要用于发送http请求来进行接口自动化
window系统使用cmd打开命令窗口,安装命令pip install requests
请求
第一种方式,底层代码就是调用的第二种方式
1.requests.get(url, params=None, **kwargs) 发送get请求
2.requests.post(url, data=None, json=None, **kwargs) 发送post请求
3.requests.delete((url, **kwargs) 发送delete请求
4.requests.put(url, data=None, **kwargs) 发送put请求
第二种方式:底层代码就是调用的第三种方式
1 requests request(method, url, **kwargs) 最核心的方法
第三种方式:自动的处理有cookie关联的接口,有些关联接口用相同cookie没有cookie会报错
requests.session().request( self,
method,#请求方式
url,#请求路径
params=None, #params:查询字符串,在接口路径后面以?传递的参数,多个函数之间用&分割,
data=None, #data:表单参数,content-Type: applcaton/x-www-form-urlencoded
json=None, #json:json参数 content-Type:applcation/json
files=None, #files:文件参数content-Type:multipart/form-data
headers=None, #请求头
cookies=None, #cookies信息
---------------------------------
auth=None, #鉴权
timeout=None, #超时
allow_redirects=True, #重定向
proxies=None, #代理
hooks=None, #钩子
stream=None, #文件下载
verify=None, #证书验证
cert=None, #CA证书
)
联系:第一种调第二种,第二种调三种
区别:第三种能够自动处理有cookie关联的接口,前两种都是单独的请求(类似于jmeter的http中cookie管理器)示例
class TestApi:
csrf_token=''
# 创建一个session对象
sess=requests.session()
def test_phpwind(self):
# 使用session对象发送请求
response = TestApi().sess.request(method="get",url="http://47.107.116.139/phpwind/")
#response = requests.get("http://47.107.116.139/phpwind/")
print(response.text)
csrf_token=re.search('name="csrf_token" value="(.*?)"',response.text).group(1)
TestApi().csrf_token=csrf_token
assert "本站新帖" in response.text
def test_login(self):
data={
"username": "user",
"password": "123",
"crf_token": TestApi().csrf_token,
"back_url": "http://47.107.116.139/phpwind/",
"invite": "",
}
headerss = {
"Accept": "application/json, text/javascript,/; q=0.01",
"X-Requested-With": "XMLHttpRequest"
}
# 使用session对象发送请求
response = TestApi().sess.request(method="post",url="http://47.107.116.139/phpwind/index.php?m=u&c=login&a=dorun",data=data,headers=headerss)
# response = requests.post("http://47.107.116.139/phpwind/index.php?m=u&c=login&a=dorun",data=data,headers=headerss)
print(response.json())
四、发送请求细节详解
(1)params传参:查询字符串参数
传参方式:在接口路径后面以?传递的参数,多个参数之间用&分隔
传参细节:对参数内容进行了urlencode编码(内容以ASCII的方式呈现
class TestApi:
def test_login(self ):
params={
"username":"admin",
"password":"123456"
}
response = requests.get("http://127.0.0.1:8000/api/login/",params=params)
(2)data传参:Form表单参数
1.添加了一个请求头:Content-Type:application/x-www.form-urlencoded
2.对参数内容进行了urlencode编码(内容以ASCII的方式呈现)
class TestApi:
def test_login(self ):
data={
"username":"admin",
"password":"123456"
}
response = requests.get("http://127.0.0.1:8000/api/login/",data=data)
(3)json传参:json参数
1.添加请求头:Content-Type:application/json
2.对参数进行:Unicode编码
class TestApi:
def test_login(self ):
data={
"username":"admin",
"password":"123456"
}
response = requests.get("http://127.0.0.1:8000/api/login/",json=data)
(4)files传参:文件参数
1.添加请求头:Content-Type:multipart/form-data
2.构建了一个表单数据,把文件的内容以二进制的方式加到body里面,
两种文件上传的方式:
第一种:打开一个已经存在的文件上传
class TestApi:
def test_login(self ):
data={
"upload":open("C:\\Users\\1.jpg","rb")
}
response = requests.get("http://127.0.0.1:8000/api/login/",files=data)
第二种:自己构造(图片name,文件内容,上传格式)
def test_login(self):
data = {
"upload": ("a.png", "文件内容", "image/jpg")
}
response = requests.get("http://127.0.0.1:8000/api/login/", files=data)
上传多个文件
def test_login(self):
data = {
"upload": ("a.png", "文件内容", "image/jpg")
"upload":open("C:\\Users\\1.jpg","rb")
}
response = requests.get("http://127.0.0.1:8000/api/login/", files=data)
同时传递表单参数和文件参数:(表单不会进行urlencode骗码)
def test_login(self):
data1 = {
"upload": ("a.png", "文件内容", "image/jpg")
}
data2= {
"username": "zhuyifan",
"password": "123456"
}
response = requests.get("http://127.0.0.1:8000/api/login/", data=data2,files=data1)
五、requests响应
响应:response对象
rep=requests.request()
#返回字符串的数据
print(rep.text)
#返回二进制字节格式的数据
print(rep.content)
#返回字典格式的数据,除了他是方法其他全是属性
print(rep.json())
#状态码
print(rep.status_code)
#返回状态信息
print(rep.reason)
#返回cookie信息
print(rep.cookies)
#返回编码格式
print(rep.encoding)
#返回响应头信息
print(rep.headers)
#返回请求方式
print(rep.method)
#返回请求路径
print(rep.url)
#返回请求数据
print(rep.body)
六、接口自动化测试实战
简介
线性脚本:NO
unittest、pytest用例管理框架
前提:pip install pytest
pytest默认规则:
1.Py文件必须以test开头或者_test结尾。
2.类名必须以Test开头
3.测试用例必须以test _开头
发送接口步骤
1.import pytest 导入pytest框架
import requests 导入requests库
requests.post()发送post请求,括号中需要填写url、data或者json,其他的包括必填的请求头
发送post请求data和json区别
区别:
在发送POST请求时,data
和json
都是requests
库中用于传递请求体的参数。然而,它们在使用和期望的输入格式上有所不同。
data
参数:data
参数期望接收一个字符串或一个元组列表(key, value)
对,通常用于发送表单编码的数据(application/x-www-form-urlencoded
)。- 如果你传递一个字典给
data
,requests
库会自动将其转换为查询字符串,并且设置Content-Type
为application/x-www-form-urlencoded
。 - 例如:
payload = {'key1': 'value1', 'key2': 'value2'}
response = requests.post(url, data=payload)
json
参数:json
参数期望接收一个Python对象(通常是字典或列表),requests
库会将其自动序列化为JSON格式,并且设置Content-Type
为application/json
。- 使用
json
参数时,你不需要手动序列化Python对象,requests
会为你处理这个转换。 - 例如:
payload = {'key1': 'value1', 'key2': 'value2'}
response = requests.post(url, json=payload)
- 格式:
data
期望表单编码的数据,而json
期望JSON格式的数据。 - Content-Type:
data
通常设置Content-Type
为application/x-www-form-urlencoded
,而json
设置Content-Type
为application/json
。 - 序列化:当你使用
data
参数传递字典时,requests
会自动将其转换为查询字符串;而当你使用json
参数时,requests
会自动将Python对象序列化为JSON字符串。
总结:
你应该根据API的要求和你的数据类型选择使用 data
还是 json
。如果你发送的是JSON格式的数据,并且API期望接收 application/json
类型的请求体,那么你应该使用 json
参数。如果你发送的是表单编码的数据,那么你应该使用 data
参数。如果你不确定,应该查看API的文档来确定正确的参数和格式。
格式错误互相转换
json.dumps(data) 序列化 把字典格式的数据转换为str格式
json.loads(data) 反序列化 把str格式转换为字典格式
总结:
使用data只能传简单的只有键值对的dict或者str格式。json一般只能传dict格式,简单的复杂的和嵌套的都可以
接口关联
:第1个接口的返回值中的数据作为第2个接口的参数。
正则表达式:
使用re库,只能提取字符串的数据,
re.search:提取一个值,得到的是一个对象,通过下标group(1)取值,如果没有匹配列值则返回None
re.findall:提取多个值,得到的是一个列表,通过下标【1】取值,,如果没有匹配到值可返回None
import re
class TestApi:
def test_phpwind(self):
response = requests.get("http://47.107.116.139/phpwind/")
print(response.text)
search=re.search('name="csrf_token" value="(.*?)"',response.text).group(1)
print(search)
findall=re.findall('name="csrf_token" value="(.*?)"',response.text)[0]
print(findall)
然后进行接口关联
class TestApi:
#通过类变量
csrf_token=''
def test_phpwind(self):
response = requests.get("http://47.107.116.139/phpwind/")
print(response.text)
csrf_token=re.search('name="csrf_token" value="(.*?)"',response.text).group(1)
TestApi().csrf_token=csrf_token
assert "本站新帖" in response.text
def test_login(self):
data={
"username": "zhangshang",
"password": "123",
"crf_token": TestApi().csrf_token,
"back_url": "http://47.107.116.139/phpwind/index.php",
"invite": "",
}
headerss = {
"Accept": "application/json, text/javascript,/;q=0.01",
"X-Requested-With": "XMLHttpRequest"
}
response = requests.post("http://47.107.116.139/phpwind/index.php?m=u&c=login&a=dorun",data=data,headers=headerss)
Jsonpath表达式:jsonpath
方法:jsonpath.jsonpath ,通过下标[1]取值,,如果没有匹配到值可返回None
{access_loken': 72_Elind8uyQPa_bGMm3nNEc_ebvrRhIVR8910-0BA6DIKeyky INOigXt4far1to9qloUWzryTBws-EWItFNFmN1XRFUF1INW-k6l69c-yfPlmBOqdmTFFJaV3DgANSEAEAVIN', 'expires_in': 7200, mashang:[{name":"百里"}.{"name":"北凡"}"}
语法规则:
(1) $根节点
(2).取子节点
$.access token
$.expires in
(3)取列表中的值
$.mashang[0] ={name":"百里"} $.mashang[0].name = 百里
$.mashang[1] ={"name":"北凡"}
(4).递归取值,无论几层都取出来
$..name =["百里”,“北凡”]
jsonpath.jsonpath(response.json(),'$..msg')[0]
七、接口自动化封装
第一步
核心是requests request() 最核心的方法,
将发送请求统一改成requests.request
通过session实现cookie鉴权,
用一个会话去发送请求,其中的东西都不会断,包括里边的cookie,就相当于用电话不用短信
更改前
以下是引用的token转化的类变量
将cookie转化为类变量
更改后