应用程序编程接口(API:Application Programming Interface):以HTTP协议形式提供,定义了输入、输出、功能描述的服务
即是规矩的制定
流程:
1、接口的功能测试(先要保证接口是正确的)
2、测试接口的数据(传递一些特殊的数据,保证接口没有问题)----比如淘宝上不能搜索到一些国家禁止售卖的东西
3、自动化测试脚本的编辑
4、接口的性能、压力测试
HTTP协议:
协议:计算机通信网络中两台电脑 之间进行通信所必须共同遵守的规则或规定
HTTP协议:超文本传输协议,是一种规定了浏览器和服务器之间通信的规则
URL(统一资源定位符)
概念:互联网上源源的地址、位置、每一个资源都有一个唯一的URL
格式:协议://主机地址/路径
GET请求
提交的数据显示在地址栏,不安全;提交的数据量有限制,不重要的的数据使用 GET
比如在淘宝搜索商品时,地址栏会出现商品名字
POST请求
隐式提交数据,更安全;没有数据量大小的限制;重要数据使用POST
数据传递的方式:
键值对: ?xx=11&xx=22 问号前面是网址,后面是传的值 &前面是属性名,后面是属性值
Json数据:
{
"属性名1":"属性值1",
"属性名2":"属性值2",
"属性名3":"属性值3"
}
HTTP协议之常见响应状态码
1xx:指示信息---表示请求已接收,继续处理
2xx:成功 ---表示请求已被成功接收,理解、接受
3xx:重定向---要完成请求必须进行更一步操作
4xx: 客户端错误---请求有语法错误或请求无法实现
5xx:服务器端错误---服务器未能实现合法的请求
restful风格:
按照一定的规则写出的易读、易懂的api文档;目的是让前端、后端、测试三方在工作的时候有据可循,提升开发的测试的效率(非常制要求)
增删改查四大功能的语法风格:
1、查
方法:get
响应码:200+查询的数据
2、增
方法:post
响应码:201+新增的数据
3、改
方法:put
响应码:200或201 +修改后的数据
4、删
方法:delete
响应码:204 (后面不加东西)
接口测试“聚合数据”地址
https://www.juhe.cn/ucenter/account
13480.....47 maggic108222
接口自动化测试
2种方式:jmeter和使用python写代码的方式(使用request库)
一、request库
1、安装和查看效果:
pip install requests
pip show requests
2、发送请求
常见的HTTP请求方式:GET、POST、PUT、DELETE、HEAD、OPTIONS
使用requests发送网络请求只需要调用HTTP请求所对应的方法即可
2.1 GET请求
import requests;
response=requests.get()
请求方法的返回值response为Response对象,我们可以从这个对象中获取 所有我们想要的响应信息
实例:
"""
请求方法:GET
响应:
响应对象.url #获取请求url
响应对象.status_code #获取响应状态码
响应对象.text #以文本形式显示响应内容
"""
import requests;
url='http://baidu.com'
#调用GET
response=requests.get(url);
#获取请求url
print('请求url是:',response.url);
#获取响应状态码
print("状态码:",response.status_code);
#获取响应信息 文本形式
print("文本响应内容",response.text);
结果:
请求url是: http://baidu.com/
状态码: 200
文本响应内容 <html>
<meta http-equiv="refresh" content="0;url=http://www.baidu.com/">
</html>
GET方法带参使用:
参数:params
方式1:params={"id":1001}
方式2:params={"id":"1001,1002"}
方式3:params={"id":1001,"kw":"北京"}
"""
请求方法:GET
案例:
1、http://www.baidu.com?id=1001
2、http://www.baidu.com?id=1001,1002
3、http://www.baidu.com?id=1001&kw=北京
参数:
params:字典或字符串
响应:
响应对象.url #获取请求url
响应对象.status_code #获取响应状态码
响应对象.text #以文本形式显示响应内容
"""
import requests;
url='http://baidu.com'
#案例1 定义字典
# params={"id":1001}
#案例2 定义列表
# params={"id":"1001,1002"}
#运行的结果是:http://baidu.com/?id=1001%2C1002 %2c是逗号
#案例3 定义列表
params={"id":1001,"kw":"北京"}
#结果:请求url是: http://baidu.com/?id=1001&kw=%E5%8C%97%E4%BA%AC
#调用GET
#请求时带参 params
response=requests.get(url,params=params);
#获取请求url
print('请求url是:',response.url);
#获取响应状态码
print("状态码:",response.status_code);
#获取响应信息 文本形式
print("文本响应内容",response.text);
2.2 POST请求
作用:新增资源
应用:
1)导包
2)调用POST方法 requests.post()
参数:
1)url
2) json 新增请求报文
3) Headers: 请求信息头
案例:
"""
案例:新增学院
参数:
1、json:传入json字符串
2、headers:传入请求信息头内容
响应:
1、响应对象.json()
接口文档里的内容:
2.1 学院--新增
1)请求方法:POST
2)请求地址:http://v.juhe.cn/weather/index #其实地址不是正确的,因为我本来就没有这个项目,这个是随手拿的天气预报的接口地址
3)请求信息头:Content-Type:application/json
4)调用传入的json串如下(可新增多条,之间用,号隔开)
{
"data":[
{
"dep_id":"T01",
"dep_name":"Test学院",
"master_name":"Test-Master",
"slogan":"Here is Slogan"
}
]
}
"""
#1、导包
import requests;
#调用post
#请求url
url='http://v.juhe.cn/weather/index'
#请求headers
headers={"Content-Type": "application/json"}
#请求json
data = {
"data": [
{
"dep_id": "T01",
"dep_name": "Test学院",
"master_name": "Test-Master",
"slogan": "Here is Slogan"
}
]
}
response=requests.post(url,json=data,headers=headers)
#获取响应对象
print(response.json())
#获取响应状态码
print(response.status_code);
运行结果:
如果是真实存在一个学院管理系统,那结果应该是长这样子的:
{"already_exist":{'count':0, 'results':[]}, 'create_success':{'count':1, 'results':[{"dep_id": "T01", "dep_name": "Test学院", "master_name": "Test-Master", "slogan": "Here is Slogan"}]}}
201
但是因为我其实拿的是天气预报的接口地址,然后参数又是拿安全的参数,所以最终的结果是“错误的请求KEY",如下所示
{'error_code': 10001, 'resultcode': '101', 'reason': '错误的请求KEY', 'result': None}
200
扩展1:
data与json的区别
data:字典对象
json:json字符串
提示:
在python中字典对象和json字符串长得一样,但是后台格式不同,因此直接使用data会报错
转换:
导入json
使用json.dumps(字典对象) 方法来转换
实例:
"""
案例:新增学院
参数:
1、json:传入json字符串
2、headers:传入请求信息头内容
响应:
1、响应对象.json()
接口文档里的内容:
2.1 学院--新增
1)请求方法:POST
2)请求地址:http://127.0.0.1:8000/api/departments
3)请求信息头:Content-Type:application/json
4)调用传入的json串如下(可新增多条,之间用,号隔开)
{
"data":[
{
"dep_id":"T01",
"dep_name":"Test学院",
"master_name":"Test-Master",
"slogan":"Here is Slogan"
}
]
}
"""
#1、导包
import json
import requests;
#调用post
#请求url
url='http://v.juhe.cn/weather/index'
#请求headers
headers={"Content-Type": "application/json"}
#请求json
data = {
"data": [
{
"dep_id": "T01",
"dep_name": "Test学院",
"master_name": "Test-Master",
"slogan": "Here is Slogan"
}
]
}
# response=requests.post(url,json=data,headers=headers);
#如果使用data,则会报Json parse error。直接这样子写是不行的,需要将字典对象转换成json字符串
#response=requests.post(url,data=data,headers=headers);
#将字黄对象转换为json字符串
response=requests.post(url,data=json.dumps(data),headers=headers);
#获取响应对象
print(response.json())
#获取响应状态码
print(response.status_code);
扩展2:响应数据.text与响应数据.json的区别
长相基本一致,但r.json的类型是字典,可以通过键名获取值;r.text只是一个字符串
如果想要取值就要使用.json的方式
2.3 PUT方法 更新资源
应用:
1)导包 import requests
2) 调用put方法 requests.put()
实例
#1、导包
import json
import requests;
#调用post
#请求url
#url='http://v.juhe.cn/weather/index'
#案例的地址原来是这样子的
# url="http://127.0.0.1:8000/api/departments"
#然后因为是要PUT(更新一条学院信息),需要在url后加加上具体的dep_id
url="http://127.0.0.1:8000/api/departments/T01"
#请求headers
headers={"Content-Type": "application/json"}
#请求json
data = {
"data": [
{
"dep_id": "T01",
"dep_name": "Test学院2", #假设需要改学院的名称,则在此作出改动
"master_name": "Test-Master",
"slogan": "Here is Slogan"
}
]
}
response=requests.put(url,data=json.dumps(data),headers=headers);
#获取响应对象
print(response.json())
#获取响应状态码
print(response.status_code);
2.4 DELETE 删除
url="http://127.0.0.1:8000/api/departments/T01"
response=requests.delete(url)
#获取响应状态码
print(response.status_code);
响应内容:
response.status_code 状态码
response.url
response.encoding 查看响应头部字符编码
response.headers 头信息
response.cookies
response.text
response.content 字节形式的响应内容
response.json() JSON形式的 响应内容
实例1:
"""
目标:响应对象常用方法
1.encodeing
1)获取请求编码
2)设置响应编码
2.headers
1)获取响应信息头信息
"""
#1、导包
import requests;
#请求url
url="http://www.baidu.com"
#调用get方法
response=requests.get(url)
#查看默认请求编码
print(response.encoding);
#查看响应内容 text
print(response.text);#汉字会变成乱码
#设置响应编码
response.encoding='utf-8';
print(response.encoding);
print(response.text)
#查看响应信息头 实际工作中比较重要,项目中一般服务器返回的token、session相头信息都在headers中
print(response.headers)
实例2:
"""
目标:响应对象常用方法
1.cookies
1)获取响应cookies信息
2.content 字节形式的响应内容
1)图片、视频。。。多媒体格式
"""
#1、导包
import requests;
#请求url
# url="http://www.baidu.com"
url_img='http://www.baidu.com/img/bd_logo1.png?where=super'
#调用get方法
response=requests.get(url_img)
#查看响应cookies 返回一个字典对象
# print("cookies:",response.cookies);
# #通过建名获取响应的cookies的值
# print("cookies信息值为:",response.cookies['BDORZ']);
#以text文本形式解析图片
# print(response.text); #结果是一堆黑色乱码
#以content形式
print(response.content);#图片流
#b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x02\x1c\x00
#将图片写入当前目录baidu.png
with open('./baidu.png','wb') as f:
f.write(response.content); #当前目录下会多出一张图片
三、案例
1、怎么获取相关数据(接口地址、数据)
1.1、获取验证码:
在验证码图片上右击鼠标选择复制图片地址:
http://localhost/index.php/Home/User/verify.html
1.2、登录接口获取:
url:勾选preserver log选项(才能抓取受保护数据-302重定向之前的数据)
form data : 以字典形式编写
登录接口:http://localhost/index.php?m=Home&c=User&a=do_login
登录数据:
data = {"username":"1348025***",
"password: ":"ma****",
"referurl":"http://localhost/index.php/home/Goods/goodsInfo/id/1",
"verify_code":"e75i"
}
1.3 订单请求获取
登录成功,点击“我的订单”---复制
http://localhost/index.php/Home/Order/order_list.html
实例:
import requests
url_login = "http://localhost/index.php?m=Home&c=User&a=do_login";
data = {"username":"13480*****",
"password: ":"******",
"referurl":"http://localhost/index.php/home/Goods/goodsInfo/id/1",
"verify_code":"e75i"
}
r = requests.post(url=url_login,data=data);
print(r.json());
运行结果:
{'status': 0, 'msg': '验证码错误'}
怎么解决验证码错误:cookie
实例:登录测试窝并查看博客
import requests
#请求验证码
url_verify_code = "https://www.testwo.com/user/captcha" #这个是右键复制图片地址
r = requests.get(url_verify_code);
#获取cookies
r_cookie = r.cookies;
print("获取的cookies:",r_cookie);
# #设置cookies变量
cookies = {"tw_ct":r_cookie['tw_ct']}
# #调用post + 添加cookies
url_login = "https://www.testwo.com/user/login"
data = {
"referer":"none",
"name":"******", //真实用户名
"password":"******", //真实密码
"captcha":"bkst"
}
r = requests.post(url=url_login,data=data,cookies=cookies);
print("返回的是:",r);
url_blog ="https://www.testwo.com/space/blog/2822"
r = requests.get(url=url_blog,data=data,cookies=cookies);
print("博客信息是:",r.text)
运行结果是:
获取的cookies: <RequestsCookieJar[<Cookie tw_ct=A3F5CA041EEFFECEAEB21DA4BFE93799 for www.testwo.com/>]>
返回的是: <Response [200]>
博客信息是:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta http-equiv="Cache-Control" content="no-store" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<title>吴水荣的博客</title>
<script type="text/javascript" src="/scripts/jquery.min.js"></script>
.........
里面就是博客内容
</html>
2、使用session:可以自动保存服务器产生的cookies信息,并且自动在下一条请求时附加:
在requets里,session对象是一个非常常用的对象,它代表一次用户会话:从客户端浏览器连接服务器开始,到客户端浏览器与服务器断开
会话能让我们在跨请求时保持某些参数,比如在同一个session实例发出的所有请求之间保持cookie
2.1创建session对象
session = requests.Session();
得到session对象后,就可以调用该对象中的方法来发送请求
session.get()\session.post()\session.delete()\session.put()
提示:无论通过session对象调用哪个方法,返回结果都是response对象
实例:
import requests
#获取session对象
session =requests.session();
#请求验证码 让session对象记录cookies
url_verify_code = "https://www.testwo.com/user/captcha" #这个是右键复制图片地址
session.get(url_verify_code);
#请求登录
url_login = "https://www.testwo.com/user/login"
data = {
"referer":"none",
"name":"shui*****",
"password":"******",
"captcha":"bkst"
}
r = session.post(url=url_login,data=data);
#查看登录是否成功,返回200就成功
print("返回的是:",r);
#查看博客
url_blog ="https://www.testwo.com/space/blog/2822"
r = session.get(url=url_blog,data=data);
print("博客信息是:",r.text)
运行结果:
返回的是: <Response [200]>
博客信息是:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta http-equiv="Cache-Control" content="no-store" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<title>吴水荣的博客</title>
......
</html>
3、集成UnitTest来测试接口自动化
3.1 需求:
某网站项目登录功能的接口测试
3.2 用例设计:
ID 模块 接口名称 请求url 请求类型 请求参数类型 请求参数 预期结果 测试结果
001 登录 获取验证码 https://www.testwo.com/user/captcha GET form 获取到验证码图片
登录 登录 https://www.testwo.com/user/login POST form {"referer":"none", 登录成功
"name":"shui****",
"password":"ma***",
"captcha":"bkst"}
002 登录 获取验证码 GET form 获取到验证码图片
登录 账号不存在 https://www.testwo.com/user/login POST form {"referer":"none", 提示账号不存在
"name":"不存在的账号",
"password":"正确的密码",
"captcha":"bkst"}
003 登录 获取验证码 GET form 获取到验证码图片
登录 密码错误 https://www.testwo.com/user/login POST form {"referer":"none", 提示密码错误
"name":"shui****",
"password":"错误的密码",
"captcha":"bkst"}
3.3 搭建unittest结构
import unittest
import requests
#新建测试类
class TestLogin(unittest.TestCase):
#setUp
def setUp(self):
pass
#tearDonw
def tearDown(self):
pass
#登录成功
def test_login_success(self):
pass
#登录失败 账号不存在
def test_username_not_exist(self):
pass
#登录失败 密码错误
def test_password_error(self):
pass
具体代码:
import unittest
import requests
import json
#新建测试类
class TestLogin(unittest.TestCase):
#setUp
def setUp(self):
#获取session对象
self.session = requests.session();
#登录url
self.url_login = 'https://www.testwo.com/user/login'
#验证码url
self.url_verify = "https://www.testwo.com/user/captcha"
#tearDonw
def tearDown(self):
#关闭session
self.session.close();
#测试方法 登录成功
def test_login_success(self):
#调用session对象 获取coookies
self.session.get(self.url_verify)
#请求登录
data = {
"referer": "none",
"name": "shui.....",
"password": "m.......",
"captcha": "bkst"
}
r = self.session.post(self.url_login,data=data);
print(r.status_code)
try:
#断言
self.assertEqual("200",str(r.status_code));
except AssertionError as e:
print(e);
#登录失败 账号不存在
def test_username_not_exist(self):
data = {
"referer": "none",
"name": "sh....hahahahaha",
"password": "m.....",
"captcha": "bkst"
}
r = self.session.post(self.url_login, data=data);
try:
# 断言
self.assertEqual("账号不存在", r.json()['msg']);
except AssertionError as e:
print(e);
#登录失败 密码错误
def test_password_error(self):
data = {
"referer": "none",
"name": "shui....",
"password": "m.....",
"captcha": "bkst"
}
r = self.session.post(self.url_login, data=data);
try:
# 断言
self.assertEqual("密码错误",r.json()['msg']);
except AssertionError as e:
print(e);
if __name__ == '__mian__':
unittest.main();
#后面2种的断言那里,r.json()['msg'] 是会报错的,因为这个网站登录时本来就没有返回这种json格式的数据,但是我不知道要写什么,只有对着视频照抄了
#第一1用例那里是执行通过的
黑马头条的接口测试
一、接口测试流程:
1、需求分析
2、挑选需要做自动化测试的功能接口
3、设计测试用例
4、搭建自动化测试环境
5、设计自动化测试项目的框架
6、编写代码
7、执行测试用例
8、生成测试报告并分析结果
二、接口清单整理
1、登录接口
1.1 请求登录接口
请求:
1)请求url:http://ttapi.research.itcast.cn/app/v1_0/autoorizations
2) 请求方法:POST
3) 请求参数:Headers = {"Content-Type":"application/json"}
4)请求报文:
{"mobile":"13480909876","code":"888888"} #假设验证码是8888888
响应:
1)状态码:201
2)响应数据
{"message":"xxx"}
1.2 获取短信验证码:
1)请求url:http://ttapi.research.itcast.cn/app/v1_0/sms/codes/:mobile
(mobile要换成手机号)
2) 状态码:200
3)请求方法:GET
提示:验证码发送成功后,在手机中查找,然后填写到1.1的4)的code里(一个手机要隔一分钟才能重新发验证码)
2、获取用户频道列表
2.1 请求:
1)url:http://ttapi.research.cn/app/v1_0/user/channels
2) 方法:GET
3) 请求参数:Headers = {"Content-Type":"application/json",
"Authorization":"Bearer token信息"} #那一长串的token信息
提示:默认token有效期为2小时
2.2 响应
1)响应状态码:200
2)响应数据:{"message":"xxx"}
3、收藏文章
3.1 请求:
1)url: http://ttapi.research.cn/app/v1_0/article/collections
2) 方法:GET
3) 请求参数:Headers = {"Content-Type":"application/json",
"Authorization":"Bearer token信息"} #那一长串的token信息
4) 请求报文:{"target":文章id}
3.2 响应
1)响应状态码:201
2)响应数据:{"message":"xxx"}
4、取消收藏文章
4.1 请求:
1)url: http://ttapi.research.cn/app/v1_0/article/collections/:target
2) 方法:DELTETE
3) 请求参数:Headers = {"Content-Type":"application/x-www-form-urlencoded",
"Authorization":"Bearer token信息"} #那一长串的token信息
4) 请求报文:{"target":文章id}
4.2 响应
1)响应状态码:204
四个接口的文档(开发给出)
1、用户认证(登录注册)
基本信息:
Path: /app/v1_0/authorizations #这个是服务器的地址名,真正使用时一定要加上服务器地址名名ip号之类的
Method:POST
接口描述:
1)线上地址
http://ttapi.research.itcast.cn/app/v1_0/autoorizations
2)返回HTPP状态码
1. 201 ok
2. 400 请求参数错误
包括:参数缺失、手机号格式不正确、验证码失效等
3. 507 服务器数据库异常
3)token说明
1. token用于访问需要身份认证的普通接口,有效期2小时
2. refresh_token 用于在token过期后,获取 新的用户token,有效期14天
请求参数
Headers
参数名称 参数值 是否必须 示例 备注
Content-Type application/json 是
Body
名称 类型 是否必须 默认值 备注 其他信息
mobile string 是 手机号
code string 是 短信验证码(这是在另外一个接口)
返回数据
名称 类型 是否必须 默认值 备注 其他信息
message string 是 提示信息
data object 否 数据
--token string 是 用户token令牌
--refresh_token string 是 用于刷新token的令牌
1.5 获取短信验证码
基本信息:
Path: /app/v1_0/sms/codes/:mobile
Method : GET
接口描述
1)线上接口路径
http://ttapi.research.itcast.cn/app/v1_0/sms/codes/:mobile #这里最后一个参数应该是一个手机号,验证码会发送到手机里,抄出来就行
2)接口访问次数受限
每手机号每分钟1次
3)返回HTTP状态码
1. 200 ok
2. 404 手机号不正确
3. 429 接口访问次数受限,body数据返回
{
“message”:"Too many requests."
}
4. 507 服务器数据库异常
请求参数:
请求参数
Headers
参数名称 参数值 是否必须 示例 备注
Content-Type application/json 是
路径参数
参数名称 示例 备注
mobile 13480909876 手机号
返回数据
名称 类型 是否必须 默认值 备注 其他信息
data object 否 数据
--mobile string 是 手机号
message string 是 ok 提示信息ok
2、获取用户频道列表:
基本信息
Path:/app/v1_0/user/chanels
Method: GET
接口描述:
1)线上地址:
http://ttapi.research.cn/app/v1_0/user/channels
2)返回状态码
507 数据库错误
200 ok
3)不强制用户登录,匿名用户返回后台设置的默认频道列表
请求参数
Headers
参数名称 参数值 是否必须 示例 备注
Content-type application/json 是
Authoization 是 Bearer
eyJ0eXA........... 用户Token,未登录用户为空
返回数据
名称 类型 是否必须 默认值 备注
message string 是 提示信息
data object 否
--channels object[] 是 频道列表
--id interger 是 频道ID
--name string 是 频道名称
3、收藏文章
基本信息:
Path: /app/v1_0/article/collections
Method: POST
接口描述:
1)线上地址:
http://ttapi.research.cn/app/v1_0/article/collections
2)返回状态码
400 请求参数错误
401 用户未认证
507 数据库错误
201 ok
3)在请求头Trace 中传递collect 埋点参数
请求参数
Headers
参数名称 参数值 是否必须 示例 备注
Content-type application/json 是
Authoization 是 Bearer
eyJ0eXA........... 用户Token,未登录用户为空
Trace 是
Body
名称 类型 是否必须 默认值 备注 其他信息
target integer 是 收藏的目标文章id
返回数据
名称 类型 是否必须 默认值 备注
message string 是 提示信息
data object 否
--target integer 是 频道列表
--id interger 是 收藏的文章id
--name string 是 频道名称
4、取消收藏文章
基本信息:
Path: /app/v1_0/article/collections/:target
Method: DELETE
接口描述:
1)线上地址:
http://ttapi.research.cn/app/v1_0/article/collections/:target #冒号后面的是文章id
2)返回状态码
204 取消成功,注意这是本接口成功调用的返回状态码,body没有数据(没有默认的message)
401 用户未认证
404 访问路径错误
507 数据库错误
201 ok
请求参数
Headers
参数名称 参数值 是否必须 示例 备注
Content-type application/x-
www-from-urlencoded是
Authoization 是 Bearer
eyJ0eXA........... 用户Token,未登录用户为空
路径参数
参数名称 示例 备注
target
返回数据
名称 类型 是否必须 默认值 备注
message string 否 提示信息
三、用例设计
3.1 单接口
模板:id、模块、接口名称、请求url、用例名称、请求方法、请求参数类型、请求参数、预期结果、备注
注意:单接口颗粒度放得比较小(以测试数据为颗粒度)
3.2 多接口
多接口颗粒度为每个接口为基础,每个接口内所设计的数据可以通过参数化来解决
实例:
ID 模块 用例名称 接口名称 请求url 请求方法 请求参数类型 请求参数 预期结果 测试结果 备注
hmtt001 登录 登录成功 获取短信验证码 http://ttapi.research.itcast.cn GET url 手机号: 1、响应状态码:200
/app/v1_0/sms/codes/:mobile 13480251222 2、手机接收到验证码
POST 1、Headers 1、Headers = {"Content-Type":"application/json"}1、响应状态码:201
请求登录接口 http://ttapi.research.itcast.cn/ 2、body 2、body={"mobile":"13480909876","code":"888888"} 2、登录成功,提示token信息
app/v1_0/authorizations
------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------
hmtt002 用户 获取用户 获取用户频道 http://ttapi.research.itcast.cn GET 1、响应状态码:200
频道列表 列表 /app/v1_0/user/channels Headers Headers = {"Content-Type":"application/json", 2、响应数据:{"message":"xxx"}
"Authorization":"Bearer token信息"}
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
hmtt003 文章 成功收藏文章 收藏文章 http://ttapi.research.itcast.cn 1、响应状态码:200
/app/v1_0/article/collections POST 1、Headers 1、Headers = {"Content-Type":"application/json",
"Authorization":"Bearer token信息"} 1、响应状态码:201
2、body 2、body={"target":"文章id"} 2、响应数据:{"message":"xxx"}
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
hmtt004 文章 成功取消收藏文章 取消收藏文章 http://ttapi.research.itcast.cn DELETE 1、url 1、响应状态码:204
/app/v1_0/article/collections/:target
2、Headers 1、Headers = {"Content-Type":"application/x-www-form-urlencoded",
"Autorization":"Bearer token信息"}
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
四、项目目录结构搭建:
1、接口对象层 api
2、用例执行业务层 case
3、数据驱动 data
4、测试报告 report
5、工具层 tools 第三方工具包
6、运行入口层 运行测试用例,并生成报告 run_suite.py
4.1
login.py
"""
目标:实现登录接口对象封装
"""
#导包 requests
import requests
#新建类 登录接口对象
class ApiLogin(object):
#新建方法 登录方法
def api_post_login(self,url,mobile,code):
#headers 定义
headers = {"Content-Type":"application/json"}
#data 定义
data = {"mobile":mobile,"code":code}
#调用post并返回响应对象
return requests.post(url,headers=headers,json=data);
4.2
case/test_login.py
"""
目标:完成登录业务层实现
"""
#导包
import unittest
from api.api_login import ApiLogin;
#新建测试类
class TestLogin(unittest.TestCase):
#新建测试方法
def test_login(self):
#暂时存放数据 url mobile code
url="http://ttapi.research.itcast.cn/app/v1_0/authorizations";
mobile = '13480。。。';
code = "189598" //这里需要先在浏览器输入http://ttapi.research.itcast.cn/app/v1_0/sms/codes/13480。。。来获取验证码,然后才开始执行
#调用登录方法
s = ApiLogin().api_post_login(url,mobile,code);
print("响应结果:",s.json())
#断言 响应信息 状态码
self.assertEqual("OK",s.json()['message']);
self.assertEqual(201,s.status_code);
if __name__ == '__main__':
unittest.main();
4.3
解决数据存储问题:
1、步骤:
编写数据存储文件 login.json
编写读取json工具
使用参数化动态获取参数数据
login.json
{
"url": "http://ttapi.research.itcast.cn/app/v1_0/authorizations",
"mobile": "13480。。。",
"code": "", ------这个先放空着,等手机收到验证码再填上去
"expect_result": "OK",
"status_code": 201
}
read_json.py
#导包
import json
#打开json文件并获取文件流
# with open("../data/login.json",'r',encoding="utf-8") as f:
# #调用load方法加载文件流
# data = json.load(f);
# print("获取的数据为:",data);
"""
问题:未经过封闭无法在别的模块内使用
解决:进行封装
"""
# def read_json():
# with open("../data/login.json", 'r', encoding="utf-8") as f:
# #调用load方法加载文件流
# return json.load(f);
"""
问题2:数据存储文件有好几个
解决:使用参数替换静态文件名
"""
class ReadJson(object):
def __init__(self,filename):
self.filepath = "../data/"+filename;
def read_json(self):
with open(self.filepath, 'r', encoding="utf-8") as f:
#调用load方法加载文件流
return json.load(f);
"""
问题3:预期格式为列表嵌套元组,目前返回字典
解决:读取字典内容,并添加到新的列表中
"""
if __name__ == "__main__":
# print(ReadJson("login.json").read_json());
data = ReadJson('login.json').read_json();
#新建空列表,添加读取json数据
arrs = [];
arrs.append((data.get("url"),
data.get("mobile"),
data.get('code'),
data.get('expect_result'),
data.get('status_code')))
print(arrs);
test_login.py
"""
目标:完成登录业务层实现
"""
#导包
import unittest
from api.api_login import ApiLogin;
from parameterized import parameterized;
from tools.read_json import ReadJson
#读取数据函数
def get_data():
data = ReadJson('login.json').read_json();
#新建空列表,添加读取json数据
arrs = [];
arrs.append((data.get("url"),
data.get("mobile"),
data.get('code'),
data.get('expect_result'),
data.get('status_code')))
return arrs;
#新建测试类
class TestLogin(unittest.TestCase):
#新建测试方法
@parameterized.expand(get_data())
def test_login(self,url,mobile,code,expect_result,status_code):
#暂时存放数据 url mobile code
# url="http://ttapi.research.itcast.cn/app/v1_0/authorizations";
# mobile = '13480。。。';
# code = "189598"
#调用登录方法
s = ApiLogin().api_post_login(url,mobile,code);
print("响应结果:",s.json())
#断言 响应信息 状态码
self.assertEqual(expect_result,s.json()['message']);
self.assertEqual(status_code,s.status_code);
if __name__ == '__main__':
unittest.main();
多个用例:
login_more.json
{
"login_001": {
"url": "http://ttapi.research.itcast.cn/app/v1_0/authorizations",
"mobile": "13480。。。。",
"code": "579120",
"expect_result": "OK",
"status_code": 201},
"login_002": {
"url": "http://ttapi.research.itcast.cn/app/v1_0/authorizations",
"mobile": "1344000",
"code": "081897",
"expect_result": "err",
"status_code": 201}
}
test_login_more.py
"""
目标:完成登录业务层实现
"""
#导包
import unittest
from api.api_login import ApiLogin;
from parameterized import parameterized;
from tools.read_json import ReadJson
#读取数据函数
def get_data():
datas = ReadJson('login_more.json').read_json();
#新建空列表,添加读取json数据
arrs = [];
#使用遍历获取所有的value
for data in datas.values():
arrs.append((data.get("url"),
data.get("mobile"),
data.get('code'),
data.get('expect_result'),
data.get('status_code')))
return arrs;
#新建测试类
class TestLogin(unittest.TestCase):
#新建测试方法
@parameterized.expand(get_data())
def test_login(self,url,mobile,code,expect_result,status_code):
#调用登录方法
s = ApiLogin().api_post_login(url,mobile,code);
print("响应结果:",s.json())
#断言 响应信息 状态码
self.assertEqual(expect_result,s.json()['message']);
self.assertEqual(status_code,s.status_code);
if __name__ == '__main__':
unittest.main();
其中第一个用例是参数是全对的,第二个用例的参数,手机号错误
所以运行结果:
响应结果: {'data': {'refresh_token': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTQ3MTIwMTUsInVzZXJfaWQiOjEyNzc3Njg4MjUxNjQ1Mjk2NjQsInJlZnJlc2giOnRydWV9.gHBTtlRLAVd-cqg46yepaOmsJDcSubskn3N6Vh8Wc-U', 'token': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTM1MDk2MTUsInVzZXJfaWQiOjEyNzc3Njg4MjUxNjQ1Mjk2NjQsInJlZnJlc2giOmZhbHNlfQ.uWM4pDRT0WcuR-WcFQgmgwOrXyqDjt9f2D-zz3TN4rA'}, 'message': 'OK'}
响应结果: {'message': {'mobile': '1348025124000 is not a valid mobile'}}
Ran 2 tests in 17.841s
FAILED (failures=1)
{'mobile': '1348025124000 is not a valid mobile'} != err
Expected :err
Actual :{'mobile': '1348025124000 is not a valid mobile'}
获取用户频道列表api封装
api_channel.py
"""
目标:实现获取用户频道列表接口对象封装
"""
#导包 requests
import requests
#新建类 对象类
class ApiChannels(object):
#新建方法 登录方法
def api_get_channels(self,url,headers):
#调用get请求
return requests.get(url,headers=headers);
test_channel.py
#导包
import unittest
from api.api_channel import ApiChannels;
#新建测试类
class TestChannels(unittest.TestCase):
#新建测试方法
def test_channels(self):
#临时数据
url = "http://ttapi.research.itcast.cn/app/v1_0/user/channels"
#提示:token之前有个空格和Bearer
headers = {"Content-Type":"pplication/json","Authorization":"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTM1MDk2MTUsInVzZXJfaWQiOjEyNzc3Njg4MjUxNjQ1Mjk2NjQsInJlZnJlc2giOmZhbHNlfQ.uWM4pDRT0WcuR-WcFQgmgwOrXyqDjt9f2D-zz3TN4rA"}
#调用获取用户频道列表方法
r = ApiChannels().api_get_channels(url,headers)
#调试
print(r.json());
#断言 状态码
self.assertEqual(200,r.status_code);
#断言 响应信息
self.assertEqual("OK",r.json()['message'])
if __name__ == "__main__":
unittest.main();
运行结果:
OK
{'message': 'OK', 'data': {'channels': [{'name': '推荐', 'id': 0}]}}
获取用户频道业务数据参数化
data/channel.json
{
"url": "http://ttapi.research.itcast.cn/app/v1_0/user/channels",
"headers": {"Content-Type":"application/json","Authorization":"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTM1NzI5NTgsInVzZXJfaWQiOjEyNzc3Njg4MjUxNjQ1Mjk2NjQsInJlZnJlc2giOmZhbHNlfQ.RpYg9aSuvp1SFzpb9d8AH-vVp-0JB3U5PMCed2a3Mjc"},
"expect_result": "OK",
"status_code": 200
}
test_channel.py
#导包
import unittest
from api.api_channel import ApiChannels;
from parameterized import parameterized;
from tools.read_json import ReadJson
def get_data():
data = ReadJson('channel.json').read_json();
# 新建空列表,添加读取json数据
arrs = [];
arrs.append((data.get("url"),
data.get("headers"),
data.get('expect_result'),
data.get('status_code')))
return arrs;
#新建测试类
class TestChannels(unittest.TestCase):
#新建测试方法
@parameterized.expand(get_data())
def test_channels(self,url,headers,expect_result,status_code):
#调用获取用户频道列表方法
r = ApiChannels().api_get_channels(url,headers)
#调试
print(r.json());
#断言 状态码
self.assertEqual(status_code, r.status_code);
#断言 响应信息
self.assertEqual(expect_result, r.json()['message'])
if __name__ == "__main__":
unittest.main();
运行结果:
OK
{'data': {'channels': [{'id': 0, 'name': '推荐'}]}, 'message': 'OK'}
运行主入口实现
先在tools那里添加HTMLTestRunner.py文件,然后在run_suite.py里导入
"""
目标:
1、搜索组装测试套件
2、运行测试套件并生成测试报告
"""
#导包
import unittest
import time
from tools.HTMLTestRunner import HTMLTestRunner
#组装测试套件
suite = unittest.defaultTestLoader.discover("./case/",pattern="test*.py");
#指定报告存放路径及文件名称
file_path = "./report/{}.html".format(time.strftime("%Y_%m_%d %H_%M_%S"));
#运行测试套件并生成测试报告
with open(file_path,'wb') as f:
HTMLTestRunner(stream=f).run(suite);