Python+Requests实现接口自动化测试实战
该文章涉及到的接口可自行下载资源,在本地运行用于练习
接上一篇:掌握接口测试全流程:从概念到实战
基于python结合requests实现接口自动化
- 接口自动化概念
- 接口自动化实现
- Requests库
- requests说明
- get请求
- requests之get方法使用
- 无参get请求
- 有参get请求
- Post请求
- requests.post()
- PUT请求
- requests.put()
- DELETE请求
- requests.delete()
- requests各请求参数解析
- 方法中 json VS data VS params VS file
- params 传递查询参数
- json VS data VS file
- file 上传文件(multipart/form-data)
- json VS data
- headers
- Timeout 设置超时时间(防请求卡死)
- Verify 控制 SSL 证书验证(HTTPS 接口)
- auth
- Cookies
- 以tpshop登录为例讲解
- requests中Session
- response各响应方法解析
- Unittest集成接口自动化用例
接口自动化概念
接口测试:对系统或组件之间的接口进行测试,验证接口数据的交换、传递和控制过程,以及相互逻辑依赖关系。
接口测试自动化:让程序代替人工自动完成对接口的测试的过程。
接口自动化实现
- 工具Postman、Jemeter
- 代码(本篇是基于python语言用requests库实现接口用例,使用unittest框架对用例进行管理和执行)
为什么要使用代码?
- 工具容易上手,对代码要求低,但是在实际项目应用中有局限性
- 测试数据不好控制
- 数据无法直接读取
- 测试加密时比较繁琐
- 工具需要依赖研发提供一些SDK等
- 代码可以调用一些方法或库实现
- 扩展能力不足
- 工具的断言没有很灵活
- 代码可以自己实现
- 测试数据不好控制
Requests库
requests说明
1、Requests库是基于python,对python自带的库urllib扩展封装的库;
2、基于urllib库使用apache license协议对接口测试做了一系列封装;
3、提供了对应HTTP接口请求的几种方法GET POST PUT DELETE HEAD OPTIONS
使用步骤:
是第三方库,需要安装
1、pip install requests
安装库
2、import requests
导入库
3、根据需求使用requests中提供的方法
get请求
requests之get方法使用
-
import requests
-
requests.get(url,params)发起请求
-
- get方法返回是response对象(响应对象)
- response.url 获取请求url
- response.text 获取响应信息
- response.status_code获取响应码
无参get请求
import requests
url = "https://www.baidu.com"
response = requests.get(url)
有参get请求
有参get请求包含:url 和查询参数,查询参数在requests提供的get方法中,可以有两种方式传递:
-
- 可以把查询参数直接拼接到url中
- 静态参数,不灵活,适用于简单固定的参数
import requests# 将参数写到url参数中
url = "https://www.baidu.com?id=1001&name='goods'"
response = requests.get(url)
print(response.staus_code)
print(response.url)
print(resposne.text)
-
- 使用params传递查询参数
- params 是get方法的参数
- params 数据格式是字典
import requests# 通过params 将查询参数传递
url = "https://www.baidu.com"
params = {"id":1001,"name":"goods"}response = requests.get(url,params=params)
- 扩展多值参数查询时的params用法
- 比如同时查询id为1001 和1002的商品信息
- https://www.baidu.com?id[]=1001&id[]=1002
- https://www.baidu.com?id=1001,id=1002
具体怎么传是前后端研发约定的📝,常见的是id =1001,1002这种字符串格式,我们以此为例看看如何模拟接口请求
❌params = {“id”:[1001,1002]} 这样会被python自动拆解成id=1001&id=1002两个参数
✅params = {“id”:“1001,1002”} 这样才传递正确
- 比如同时查询id为1001 和1002的商品信息
Post请求
接口项目自行下载资源部署到本地~~
post接口请求主要考虑以下部分怎么模拟请求传递:
- 请求url
- 请求方法:post
- 请求header
- 请求体
requests.post()
- 作用:模拟post请求,添加
- 应用:
- post方法参数:
- url
- data : 传递表单格式的请求体
- json:传递json格式的请求体
- headers:传递请求体
- cookies:传递cookie
- 。。。
- 响应:是一个response对象
- response.text 获取文本格式的响应信息
- response.json() 获取json格式的响应信息,并将返回的json字符串解析成字典格式
- post方法参数:
# 1、导包
import requests
# 2、post请求
url = "http://localhost:8000/api/users"
# 2.1 json格式的请求体
json= {"name":"zx","email":"hbsdxxxxxx@xxx.com","age":18}
# 2.2 headers请求头
headers = {"Content-Type":"application/json"}
# 2.3 发送post请求
response = requests.post(url,json=json,headers=headers)# 解析响应数据
print(response.json())# 将响应的json串解析成字典格式
PUT请求
作用:在restful风格的HTTP接口中用于更新资源
参数在请求体中,同post方法一致
注意:put请求做更新值时,无论是否更新所有值都需要写全量参数
requests.put()
requests中put方法使用同post基本一致
- 作用:模拟put接口
- 应用
- 导包
import requests
requests.put()
- 方法主要参数:
- url:请求url
- data:表单格式的请求体
- json:json格式的请求体
- headers: 请求头
- 方法返回:Response对象
- response.json() 响应体json格式信息,并将信息解析成字典
- response.status_code 响应码
- 方法主要参数:
- 导包
- 示例:
import requests
res_get = requests.get(url)
print(res_get.status_code)
if res_get.status_code == 200:print("查询用户成功")print("列表信息",res_get.json())
else:print("查询失败")params_update = {"user_id":6,"name": "qzz2","email": "QZZ@163.com","age": 15
}
res_put = requests.put(url+'/6',headers=headers,json=params_update)
print(res_put.url)
print(res_put.status_code)res_get = requests.get(url)
print(res_get.status_code)
if res_get.status_code == 200:print("查询用户成功")print("列表信息",res_get.json())
else:print("查询失败")
DELETE请求
作用:在restful风格的HTTP接口中用于删除资源
一般不用传参数,如果要传递参数同get一样,拼接在url后的查询参数
requests.delete()
- 作用:模拟http接口的delete方法
- 应用:
-
导包
import requests
-
requests.delete()
主要参数:- url:
- params:
- headers
返回:Response对象
- response.status_code 响应码
-
- 示例:
import requests
url = "http://localhost:8000/api/users"
url1 = "http://localhost:8000/api/users/6"res_get = requests.get(url)
print("删除前的信息:",res_get.json())
requests.delete(url1)
res_get1 = requests.get(url)
print("删除后的信息,",res_get1.json())
requests各请求参数解析
方法中 json VS data VS params VS file
之前将http接口的请求时讲过,http请求协议包含“请求行 请求头 请求体”
-
请求行包含:请求方法 请求url 请求协议
-
请求头主要包含:
- Content-Type
- User-Agent
QA常用到的
-
请求体:put和post方法会存在。
-
get\delete方法参数是查询参数,使用?拼接在url后
requests参数中json\data\file是用于传递“请求体数据的”,params用于传递查询参数
params 传递查询参数
- 作用:传递查询参数
- 格式:字典{key:value} key是参数名 value是参数值(可以是列表、字符串、元组等)
- 会将参数解析成 key=value&key=value格式拼接到url后头发送接口请求
再来说用于传递请求体的 json data file 三者:
json VS data VS file
同样是用于传递请求体的json和data和file,有什么区别呢?
请求头中有Content-Type决定请求体的数据格式。
那么requests库中的post方法中提供两个参数json\data分别对应不同Content-Type时,传递不同格式的请求体。
file 上传文件(multipart/form-data)
- 对应Content-Type : multipart/form-data
- 作用:传递文件、图片等资源
- 参数值:支持字典格式{key:value};其中key是接口文档约定的名字、value是元组(‘文件名’,文件对象,MIME对象)
- 使用示例:
import requestswith open("../image.png",'rb') as f:requests.post(url,file={"iamge":('image.png',f,'image/jepg')},data={其他参数})
json VS data
首先要明确在python中 字典 和 json串是不一样的!!只是格式长的比较像。
字典:数据类型是dict
json:实际上是字符串
在http接口中数据大多数是通过json串传输的。
– python中标准库json库提供了序列化json.dumps(dict)
来将字典转成json串;也提供了反序列化将接收到的json数据反序列化json.loads(json_str)
– python中requests库中post\put方法中json参数也有类似功能:将字典解析成json后再发送请求requests.post(url,json=dict)
- json
- 对应Content-Type:application/json格式的请求体
- 参数值:支持字典格式
- python会自动将字典格式的数据解析成json串后再发送请求
- 并且会自动给请求加上请求头Content-Type:application/json
import requestsurl = "https://httpbin.org/post"
# JSON格式参数(字典)
json_data = {"username": "test001","password": "123456","remember_me": True # 支持布尔值、数字等类型
}# 发送POST请求(无需手动设置Content-Type)
response = requests.post(url=url, json=json_data)
print("服务器收到的JSON数据:", response.json()["json"]) # 验证服务器是否正确接收
- data
- 对应Content-Type:application/x-www-form-urlencoded格式的请求体
- 参数值:支持字典格式 或 字符串格式
- 使用时推荐显示设置请求头
- python会将data解析成key=value&key=value格式再发送请求
import requestsurl = "https://httpbin.org/post"
# 表单格式参数(字典)
data = {"username": "test001","password": "123456"
}
# 设置请求头(指定表单格式)
headers = {"Content-Type": "application/x-www-form-urlencoded"}# 发送POST请求
response = requests.post(url=url, data=data, headers=headers)
print("请求体内容:", response.json()["form"]) # 打印服务器收到的表单数据
headers
- 作用:传递请求头信息;
- 格式:字典 {key:value}
- 示例:
import pythonheaders = {"Content-type":"application/x-www-form-urlencoded",
"Authrization":"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",# token
"User-Agent":"Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)"# 模拟iPhone
}
response = requests.post(url,data=data,headers=headers)
Timeout 设置超时时间(防请求卡死)
- 作用:指定请求的最大等待时间(秒),超过则抛出Timeout异常(避免接口无响应时脚本一直卡住)。
- 格式:数字(如5表示 5 秒)或元组(连接超时, 读取超时)(分别控制 “建立连接” 和 “接收响应” 的超时)。
import requests
from requests.exceptions import Timeouttry:# 设置超时5秒(总超时)response = requests.post("https://httpbin.org/post", timeout=5)
except Timeout:print("请求超时,超过5秒未响应")
Verify 控制 SSL 证书验证(HTTPS 接口)
- 取值:True 或 False
- 作用:用于https接口中校验SSL证书,可以在测试环境中Verify设置为False 免校验
auth
- 作用:用于需要基础认证(Basic Auth)的接口,自动处理用户名和密码的加密(无需手动构造Authorization头)。
- 格式:元组(用户名, 密码)。
- 示例(访问需要 Basic 认证的接口):
import requestsurl = "https://httpbin.org/basic-auth/user/pass" # 该接口要求用户user、密码pass
# auth参数:元组(用户名, 密码)
response = requests.post(url=url, auth=("user", "pass"))
print("认证结果:", response.json()) # 成功返回{"authenticated": true, "user": "user"}
Cookies
- 作用: 手动传递cookies,模拟登录
- 参数值:支持字典格式 {“cookiename”:“cookievalue”} 或 RequestsCookieJar对象(从其他响应得到的cookie对象)
- 示例:
以tpshop登录为例讲解
http://hmshop-test.itheima.net
需求:获取订单列表自动化
自动化实现:先登录接口->再请求订单列表
模拟自动获取我的订单列表
需要以下几个接口:
1、验证码接口
GET http://hmshop-test.itheima.net/index.php?m=Home&c=User&a=verify
2、登录接口
POST http://hmshop-test.itheima.net/index.php?m=Home&c=User&a=do_login
{"username":"",
"password":"",
"verify_code":}
Content-Type=application/json
3、获取订单接口
GET http://hmshop-test.itheima.net/index.php/Home/Order/order_list.html接口逻辑:
1、先调用验证码接口,服务端返回一个cookie
2、再携带cookie信息调登录接口,服务端验证cookie和用户、密码、验证码都通过,登录成功
3、请求订单接口获取订单列表(一定是要登录状态,如果是浏览器会自动携带cookie,自动化中我们需要手动带cookie)
# 1、导包
import requests
# 2、请求验证码接口拿到cookie信息
url_code = "http://hmshop-test.itheima.net/index.php?m=Home&c=User&a=verify"
response_code= requests.get(url_code)
if resposne_code.status_code == 200:# 3、获取cookie信息cookies = response_code.cookies# 4、请求登录接口url_login = "http://hmshop-test.itheima.net/index.php?m=Home&c=User&a=do_login"headers = {"Content-Type":"applicaiton/json"}data = {"username":"your_username","password":"your_password","verify_code":8888}response_login = response.post(url_login,json=data,headers=headers,cookies=cookies)# 5、请求订单列表url_orderList = "http://hmshop-test.itheima.net/index.php/Home/Order/order_list.html"response_order = response.get(url_orderList,cookies=cookies)
else:print(f"验证码接口请求异常{response_code.status_code}")
以上是实现一个需要维系登录状态的业务接口自动化流程,可以发现像cookies这样需要在一个流程中保持的参数需要我们手动给加上,如果是在浏览器页面上操作浏览器是可以自动保存cookie和在下一次请求时自动携带cookie信息的;
requests库中的Session类提供了类似浏览器功能的方法,可以在一次会话中保持参数,我们无需手动携带了,下面我们来熟悉下requests中的Session
requests中Session
session:会话
requests中的session对象:
-
作用:在一次会话中维持请求参数,在下一次会话内请求中自动带上参数。常用于cookie,类似于浏览器的功能
-
一次会话:浏览器打开与服务器建立连接开始,到浏览器与服务器断开连接结束
-
应用:
- 导包
import requests
- 实例化session对象
session = requests.Session()
- 使用session对象发送请求
session.get() session.post()
- 关闭session对象
我们使用Session再次实现获取订单列表接口自动化:
- 导包
# 导包
import requests
# 实例化Session对象
session = requests.Session()
# 使用session对象根据需求发送请求,无需手动传cookie
url_code = "http://hmshop-test.itheima.net/index.php?m=Home&c=User&a=verify"
url_login = "http://hmshop-test.itheima.net/index.php?m=Home&c=User&a=do_login"
url_orderList = "http://hmshop-test.itheima.net/index.php/Home/Order/order_list.html"
session.get(url_code)
headers = {"Content-Type":"applicaiton/json"}
data = {"username":"your_username","password":"your_password","verify_code":8888}
session.post(url_login,json=data,headers=headers)
session.get(url_orderList)
response各响应方法解析
- response.status_code 获取响应码
- resposne.cookies 获取响应cookie
- 作用:获取服务器通过Set-Cookie头设置的 Cookie(RequestsCookieJar对象,可直接用于后续请求)。
- 示例(用登录返回的 Cookie 访问后续接口):
# 1. 登录接口:获取Cookie
login_response = requests.post("https://httpbin.org/post", json={"username": "test"})
login_cookies = login_response.cookies # 提取Cookie# 2. 后续接口:使用登录返回的Cookie(无需重新登录)
user_info_response = requests.post("https://httpbin.org/post", cookies=login_cookies)
-
response.text 获取文本格式的响应体
-
response.json() 获取json格式的响应体 并将json串转为字典
-
response.headers 获取响应头信息
- 比如content-type,setcookies等值,字典格式
response = requests.post("https://httpbin.org/post")
# 获取响应头中的Content-Type
content_type = response.headers.get("Content-Type")
print("响应数据格式:", content_type) # 输出:application/json
- response.content 获取字节流格式的响应体 比如图片或文件
# 假设该接口返回图片二进制数据
response = requests.post("https://httpbin.org/image/jpeg")
if response.status_code == 200:# 保存二进制数据到本地with open("download.jpg", "wb") as f:f.write(response.content) # 用content获取二进制内容print("图片下载成功")
- raise_for_status() 抛HTTPError
- 作用:若接口响应码是4xx或5xx 则抛HTTPError
import requests
from requests.exceptions import HTTPErrortry:response = requests.post("https://httpbin.org/status/400") # 故意返回400错误response.raise_for_status() # 状态码400,抛出异常
except HTTPError as e:print(f"接口请求错误:{e}") # 输出错误信息
- resposne.url 返回实际请求的url
Unittest集成接口自动化用例
unittest框架可查看文章unittest管理自动化用例教程
- 批量执行用例
- 断言
- 测试报告
需求:
http://hmshop-test.itheima.net
测试该网站登录功能
一、用例编写
编号 | 模块 | 用例标题 | 前提 | url | 请求方法 | 请求参数类型 | 请求参数 | 预期接口 |
---|---|---|---|---|---|---|---|---|
1 | 登录 | 登录成功 | 账号已注册 | http://hmshop-test.itheima.net/index.php?m=Home&c=User&a=do_login | POST | application/json | {“username”:“”,“password”:“”,“verify_code”:8888} | {“status”:0,“msg”:“登录成功”} |
2 | 登录 | 账号不存在 | 账号未注册 | http://hmshop-test.itheima.net/index.php?m=Home&c=User&a=do_login | POST | application/json | {“username”:“”,“password”:“”,“verify_code”:8888} | {“status”:0,“msg”:“登录成功”} |
3 | 登录 | 密码错误 | 账号已注册,错误密码 | http://hmshop-test.itheima.net/index.php?m=Home&c=User&a=do_login | POST | application/json | {“username”:“”,“password”:“”,“verify_code”:8888} | {“status”:0,“msg”:“登录成功”} |
二、框架搭建
- 导包unittest
- 创建测试类,集成Unittest.TestCase
- 创建test开头的测试用例
- TestLoader创建测试套件
- 使用HTMLTestRunner执行测试套件并生成测试报告
cases\test_login.py:
import unittest
import requests
from parameterized import parameterized
from login_test_unittest import data
import jsondef get_test_data():with open("../data/test_login.json", 'r', encoding="utf-8") as f:data = f.read()print("测试数据:", data, type(data))data_dict = json.loads(data)test_login = data_dict['test_data']test_login_list = []for item in test_login:tuple_item = (item.get('username'), item.get("password"), item.get("result"), item.get("verify_code"))# 生成列表返回test_login_list.append(tuple_item)return test_login_listclass testLogin(unittest.TestCase):def setUp(self):# 初始化操作# 初始化session会话self.session = requests.Session()# 请求验证码接口获取cookieurl_code="http://hmshop-test.itheima.net/index.php?m=Home&c=User&a=verify"self.headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"}resposne = self.session.get(url_code,headers=self.headers)print(self.session)print(resposne.cookies)def tearDown(self) -> None:# 结束后关闭会话self.session.close()# 创建测试用例@parameterized.expand(get_test_data())def test_login_success(self,username,password,result,verify_code=8888):url_login = "http://hmshop-test.itheima.net/index.php?m=Home&c=User&a=do_login"# 构造数据data = {"username":username,"password":password,"verify_code":verify_code}headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"}# 登录response = self.session.post(url_login,json=data,headers=headers)print(self.session)# 断言登录接口assert 200==response.status_code,f"接口请求异常{response.status_code}"print("接口结果",response.text)# 断言登录结果unittest.TestCase.assertEqual(self,result,response.json()['msg'],f"接口返回与预期结果不一致,预期:{result},实际:{response.json()['msg']}")if __name__ == '__main__':unittest.main()
data\test_login.json:
{"test_data": [{"username": "19831122087","password": ".","verify_code": 8888,"result": "登录成功"},
{"username": "","password": "","verify_code": 8888,"result": "验证码错误"},
{"username": "","password": "","verify_code": 8888,"result": "账号不存在!"},
{"username": "","password": "","verify_code": 8888,"result": "密码错误"}]}
runner\html_runner.py
from HTMLTestRunner import HTMLTestRunner
import unittest# 创建测试套件
suite = unittest.defaultTestLoader.discover('../cases')# 实例化runner对象
with open("reporter.html",'w',encoding='utf-8') as f:runner = HTMLTestRunner.HTMLTestRunner(stream=f, verbosity=2, title="商城接口自动化测试", description="登录接口测试报告")runner.run(suite)