按协议划分:http、tcp、IP
按语言划分:C++、java、php 。。。
按范围划分:
系统之间:
程序之间:
方法与方法之间,函数与函数之间,模块与模块之间
# 接口
def eat(something):
print(f'小鸟在吃{something}')
# 使用接口,传递数据
def bird():
eat('苹果')
bird()
原理
接口测试,主要针对的测试目标 —— 服务器
怎么测?
用什么测?
测什么?
特点
实现方式
什么是自动化接口测试?
协议:就是规则。要求通信的双方必须严格遵守!
HTTP:(HyperText Transfer Protocol)超文本传输协议,是一个基于请求与响应模式的、应用层的协议,也是互联网上应用最为广泛的一种网络协议。
特征:
请写出以下URL的各个组成部分。这个URL没有写出端口号,它的端口是什么?
http:www.dou.com:8080/index.html?key=123
协议:http
域名:www.dou.com
端口号:8080( 不是默认,只不过网络 网络通信常用!)
资源路径:/index.html
查询参数:key=123
作用:
POST http://tpshop-test.itheima.net/index.php HTTP/1.1
Host: tpshop-test.itheima.net
Connection: keep-alive
Content-Length: 53
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://tpshop-test.itheima.net
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90
Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://tpshop-test.itheima.net/Home/user/login.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: is_mobile=0; province_id=1; city_id=2; district_id=3;
is_distribut=1; PHPSESSID=1b80tii77mhle0fqd2bg52ucg5; cn=0
username=13800138006&password=123456&verify_code=8888
作用:
状态码:
状态码描述:一般与状态码 唯一对应。 200 —— ok; 404 —— file not found
HTTP/1.1 200 OK
Server: nginx
Date: Mon, 29 Jun 2020 03:36:28 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
X-Powered-By: PHP/5.6.40
Set-Cookie: is_mobile=0; expires=Mon, 29-Jun-2020 04:36:28 GMT;
Max-Age=3600; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-
check=0, pre-check=0
Pragma: no-cache
Set-Cookie: user_id=3338; path=/
Set-Cookie: is_distribut=1; path=/
Set-Cookie: uname=13800138006; path=/
Set-Cookie: cn=0; expires=Mon, 29-Jun-2020 02:36:28 GMT; Max-
Age=-3600; path=/
Content-Length: 805
{"status":1,"msg":"\u767b\u9646\u6210\u529f","result":
{"user_id":3338,"email":"","password":"519475228fe35ad067744465
c42a19b2","paypwd":null,"sex":0,"birthday":0,"user_money":"0.00
","frozen_money":"0.00","distribut_money":"0.00","underling_num
ber":0,"pay_points":100,"address_id":0,"reg_time":1590980161,"l
ast_login":1590980161,"last_ip":"","qq":"","mobile":"1380013800
6","mobile_validated":1,"oauth":"","openid":null,"unionid":null
,"head_pic":null,"province":0,"city":0,"district":0,"email_vali
dated":0,"nickname":"13800138006","level":1,"discount":"1.00","
total_amount":"0.00","is_lock":0,"is_distribut":1,"first_leader
":0,"second_leader":0,"third_leader":0,"token":"a26e177bd6506ff
f68c01fd13e742073","message_mask":63,"push_id":"0","distribut_l
evel":0,"level_name":"\u6ce8\u518c\u4f1a\u5458"},"url":""}
特点:
特点:
分析接口文档,设计编写接口测试用例
使用Postman设置请求方法、URL、请求头、请求体,向接口发送http请求,并查看响应数据
为什么写
测试点称之为测试维度。
攻击安全。 —— 与测试工程师无关。
业务安全。 —— 测试的方向。
- SQL注入:在用户能输入数据的位置,写入SQL语句。
- SQL注入安全,用户恶意写入的SQL语句,不会执行,查询数据库!
tpshop商城 登录 页面,手工功能测试用例设计要点:
- 页面布局是否符合需求
- 测试 用户名 输入框,输入的数据是否正确。
- 测试 密码 输入框,输入的数据是否正确。
- 测试 验证码 输入框, 输入的数据是否正确。
tpshop商城 登录 页面,接口测试用例设计要点:
手工 测试用例文档 8 大要素:
编号、用例名称(标题)、模块、优先级、预置条件、测试数据、操作步骤、预期结果
接口 测用例文档 10 要素:
分析接口文档,以 “登录接口” 为例:
编号 | 用例名称 | 模块 | 优先级 | 预置条件 | 请求方法 | URL | 请求头 | 请求体(请求数据) | 预期结果 |
---|---|---|---|---|---|---|---|---|---|
login_001 | 登陆成功 | 登录 | p1 | 账号已注册 | POST | {协议+域名}/api/sys/login | Content-Type:application/json | {“mobile”:“13800000002”,“password”:“123456”} | 状态码:200 {“success”:true,“code”:10000,“message”:“操作成功!”,“data”:“f5050a1b-7919-444c-9ec4-3c1a7286536d”} |
登录模块的接口测试用例测试点:
用户名相关 5 条:
login_002 | 用户名为空 | 登录 | p2 | —— | POST | {协议+域名}/api/sys/login | Content-Type:application/json | {“mobile”:“”,“password”:“123456”} | 状态码:200 {“success”:false,“code”:20001,“message”:“用户名或密码错误”,“data”:null} |
---|---|---|---|---|---|---|---|---|---|
login_003 | 用户名包含特殊字符、字母 | 登录 | p2 | —— | POST | {协议+域名}/api/sys/login | Content-Type:application/json | {“mobile”:“13800abc”,“password”:“123456”} | 状态码:200 {“success”:false,“code”:20001,“message”:“用户名或密码错误”,“data”:null} |
login_004 | 用户名超11位(12位) | 登录 | p2 | —— | POST | {协议+域名}/api/sys/login | Content-Type:application/json | {“mobile”:“138000000023”,“password”:“123456”} | 状态码:200 {“success”:false,“code”:20001,“message”:“用户名或密码错误”,“data”:null} |
login_005 | 用户名不足11位(10位) | 登录 | p2 | —— | POST | {协议+域名}/api/sys/login | Content-Type:application/json | {“mobile”:“1380000000”,“password”:“123456”} | 状态码:200 {“success”:false,“code”:20001,“message”:“用户名或密码错误”,“data”:null} |
login_006 | 用户名未注册 | 登录 | p2 | 数据库中不存在的手机号 | POST | {协议+域名}/api/sys/login | Content-Type:application/json | {“mobile”:“16700542479”,“password”:“123456”} | 状态码:200 {“success”:false,“code”:20001,“message”:“用户名或密码错误”,“data”:null} |
密码相关 5 条:
login_007 | 密码为空 | 登录 | p2 | —— | POST | {协议+域名}/api/sys/login | Content-Type:application/json | {“mobile”:“13800000002”,“password”:“”} | 状态码:200 {“success”:false,“code”:20001,“message”:“用户名或密码错误”,“data”:null} |
---|---|---|---|---|---|---|---|---|---|
login_008 | 密码包含特殊字符、字母 | 登录 | p2 | —— | POST | {协议+域名}/api/sys/login | Content-Type:application/json | {“mobile”:“13800000002”,“password”:“123&%rt”} | 状态码:200 {“success”:false,“code”:20001,“message”:“用户名或密码错误”,“data”:null} |
login_009 | 密码1位 | 登录 | p2 | —— | POST | {协议+域名}/api/sys/login | Content-Type:application/json | {“mobile”:“13800000002”,“password”:“1”} | 状态码:200 {“success”:false,“code”:20001,“message”:“用户名或密码错误”,“data”:null} |
login_010 | 密码100位 | 登录 | p2 | —— | POST | {协议+域名}/api/sys/login | Content-Type:application/json | {“mobile”:“13800000002”,“password”:“放置100位字符组成的密码”} | 状态码:200 {“success”:false,“code”:20001,“message”:“用户名或密码错误”,“data”:null} |
login_011 | 错误密码 | 登录 | p2 | —— | POST | {协议+域名}/api/sys/login | Content-Type:application/json | {“mobile”:“13800000002”,“password”:“888888”} | 状态码:200 {“success”:false,“code”:20001,“message”:“用户名或密码错误”,“data”:null} |
参数相关:
login_012 | 必选参数(全部参数) | 登录 | p2 | —— | POST | {协议+域名}/api/sys/login | Content-Type:application/json | {“mobile”:“13800000002”,“password”:“123456”} | 状态码:200 {“success”:true,“code”:10000,“message”:“操作成功!”,“data”:"f5050a1b-7919-444c-9ec4- 3c1a7286536d "} |
---|---|---|---|---|---|---|---|---|---|
login_013 | 多参 | 登录 | p2 | —— | POST | {协议+域名}/api/sys/login | Content-Type:application/json | {“abc”:“123”,“mobile”:“13800000002”,“password”:“123456”} | 状态码:200 {“success”:true,“code”:10000,“message”:“操作成功!”,“data”:"f5050a1b-7919-444c-9ec4- 3c1a7286536d "} |
login_014 | 少参(少mobile) | 登录 | p2 | —— | POST | {协议+域名}/api/sys/login | Content-Type:application/json | {“password”:“123456”} | 状态码:200 {“success”:false,“code”:20001,“message”:“用户名或密码错误”,“data”:null} |
login_015 | 无参 | 登录 | p2 | —— | POST | {协议+域名}/api/sys/login | Content-Type:application/json | {“success”:false,“code”:99999,“message”:“抱歉,系统繁忙,请稍后重试!”,“data”:null} | |
login_016 | 错误参数(mobile参数名错误) | 登录 | p2 | —— | POST | {协议+域名}/api/sys/login | Content-Type:application/json | {“abc”:“13800000002”,“password”:“123456”} | 状态码:200 {“success”:false,“code”:20001,“message”:“用户名或密码错误”,“data”:null} |
针对 “员工管理” 业务场景:
请求方法:post
URL: {协议+域名}/api/sys/user
请求头:
请求体(请求数据):{“username”:“爱因斯坦”,“mobile”:“17289432100”,“timeOfEntry”:“2021-07-12”,“formOfEmployment”:1,“departmentName”:“测试0607”,“departmentId”:“1412421425733664768”,“workNumber”:“234”,“correctionTime”:“2021-07-30T16:00:00.000Z”}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uIbGHCuL-1649906350829)(接口测试-第02天-课堂笔记.assets/image-20210708155543953.png)]
预期结果:
用例名称 | 模块 | 优先级 | 预置条件 | 请求方法 | URL | 请求头 | 请求体(请求数据) | 预期结果 |
---|---|---|---|---|---|---|---|---|
添加员工 | 员工管理 | p0 | 登录成功 | post | {协议+域名}/api/sys/user | Content-Type: application/json, Authorization: Bearer f5050a1b-7919-444c-9ec4-3c1a7286536d | {“username”:“爱因斯坦”,“mobile”:“17289432100”,“timeOfEntry”:“2021-07-12”,“formOfEmployment”:1,“departmentName”:“测试0607”,“departmentId”:“1412421425733664768”,“workNumber”:“234”,“correctionTime”:“2021-07-30T16:00:00.000Z”} | 状态码:200 {“success”:true,“code”:10000,“message”:“操作成功!”, “data”:{“id”:“113749504”}} |
emp_manager_002 | 查询员工 | 员工管理 | p1 | 登录成功 | GET | {协议+域名}/api/sys/user/:target | Content-Type: application/json, Authorization: Bearer f5050a1b-7919-444c-9ec4-3c1a7286536d | 状态码:200 {“success”: true, “code”: 10000, “message”: “操作成功!”, “data”: { 所查询的员工的详细信息} } | |
---|---|---|---|---|---|---|---|---|---|
emp_manager_003 | 修改员工 | 员工管理 | p0 | 登录成功 | PUT | {协议+域名}/api/sys/user/:target | Content-Type: application/json, Authorization: Bearer xxx | {“username”:“小猪佩奇”} | 状态码:200 {“success”:true,“code”:10000,“message”:“操作成功!”, “data”:{“id”:“xxx”}} |
---|---|---|---|---|---|---|---|---|---|
返回数据中的 id ,代表 修改的员工id (是一个会变化的数据)。
emp_manager_004 | 删除员工 | 员工管理 | p0 | 登录成功 | delete | {协议+域名} /api/sys/user/:target | Content-Type: application/json, Authorization: Bearer xxx | 状态码:200 {“success”:true,“code”:10000,“message”:“操作成功!”,“data”:null} | |
---|---|---|---|---|---|---|---|---|---|
emp_manager_005 | 查询员工列表 | 员工管理 | p0 | 登录成功 | GET | {协议+域名} /api/sys/user?page=1&size=10 | Content-Type: application/json, Authorization: Bearer xxx | 状态码:200 { “success”: true, “code”: 10000, “message”: “操作成功!”, “data”: { “total”: xxxx, “rows” [ {},{},… 10个员工的详细信息 ] } } | |
---|---|---|---|---|---|---|---|---|---|
使用 postman 发送 http请求,访问 tpshop 商城的 “搜索” 接口
访问 tpshop 商城的 “搜索” 接口所需要的信息:
—— 结合 F12 浏览器开发者工具分析:
- 请求方法:GET
- URL:http://tpshop-test.itheima.net/Home/Goods/search.html?q=%E6%89%8B%E6%9C%BA
- 请求头:无
- 请求体:无
- 返回数据:html网页
使用 Postman 向 topshop 商城 登录接口 发送一个密码错误的 登录请求
请求方法:post
URL:http://tpshop-test.itheima.net/index.php?m=Home&c=User&a=do_login&t=0.6693319462870182
请求头:Content-Type:application/x-www-form-urlencoded
请求体:
- username: 13812345678
- password: 12345699999
- verify_code : 8888
返回数据:
{“status”:-2,“msg”:“密码错误!”} ---- 实际返回结果 “验证码错误” 与业务有关,后续课程讲解。
从接口文档,获取 登录接口 的 http请求,使用 Postman 发送
使用Postman管理、执行测试用例生成测试报告
创建 collections
创建 用例集、子目录和 请求
导出
导入
Status code: Code is 200
// 断言响应状态码 是否为 200
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
pm:代表 postman 的一个实例
test():是 pm实例的一个方法。有两个参数
参数1:在断言成功后,给出的文字提示。可以修改。"Status code is 200"
参数2:匿名函数。
pm.response.to.have.status(200);
// 意思:postman 的响应结果中应该包含状态码 200
200 ——> 预期结果!
Response body: Contains string
// 断言响应体包含指定字符串
pm.test("Body matches string", function () {
pm.expect(pm.response.text()).to.include("string_you_want_to_search");
});
pm:postman的一个实例
test(): postman实例的方法,有两个参数
参1:断言后显示的文字提示信息,可改。
参2:匿名函数
pm.expect(pm.response.text()).to.include("string_you_want_to_search");
// 意思:pm 期望 响应文本 中,包含 xxxx 字符串。
"string_you_want_to_search" ——> 预期结果。 可以修改
Response body: Is equal to a string
// 断言 响应体 等于某个字符串(对象)
pm.test("Body is correct", function () {
pm.response.to.have.body("response_body_string");
});
pm.response.to.have.body("response_body_string");
// 意思是,pm 的 响应中应该有 响应体 xxx
"response_body_string" ——> 预期结果。 可以修改
Response body: JSON value check
// 断言json的响应结果
pm.test("Your test name", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.value).to.eql(100);
});
var jsonData = pm.response.json();
// var jsonData: 用js语法定义一个变量。jsonData 就是变量名
// pm.response.json(); 代表响应的json结果
/* 举例:response.json();
{
"success": true,
"code": 10000,
"message": "操作成功!",
"data": "95c78d75-721c-40fb-b2d5-742fea42cbd5"
}
*/
pm.expect(jsonData.value).to.eql(100);
// 意思:pm 预期 json结果 key对应的值 等于 xxx
// to.eql(100); 中的 100 代表预期结果。可以修改的。
/* 举例:
jsonData.value 的 value:
取 :success、code、message、data
*/
示例:
// 断言json的响应结果-success的值为true
pm.test("断言响应结果success的值为true", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.success).to.eql(true);
});
pm.test("断言响应结果中code的值为10000", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.code).to.eql(10000);
});
pm.test("断言响应结果中message的值为 操作成功", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.message).to.eql("操作成功!");
});
Response headers: Content-Type header check
// 断言响应头
pm.test("Content-Type is present", function () {
pm.response.to.have.header("Content-Type");
});
pm.response.to.have.header("Content-Type");
// pm 的响应 头中包含 Content-Type
// 示例:可以在 header 中,添加 响应头中的 key 对应的 value 判定。用 ,隔分。
// 断言响应头
pm.test("Content-Type is present", function () {
pm.response.to.have.header("Content-Type", "application/json;charset=UTF-8");
});
全局变量
概念:在 Postman 全局生效的变量,全局唯一。
设置:
代码设置:pm.globals.set(“全局变量名”,全局变量的值)
// 示例:
pm.globals.set("glb_age", 100)
获取:
代码获取: var 接收值的变量 = pm.globals.get(“全局变量名”)
// 示例:
var ret = pm.globals.get("glb_age") // ret 的值为 100
请求参数获取(postman界面获取): {{全局变量名}}
// 示例:
{{glb_age}}
环境变量
概念:在 特定环境(生产环境、开发环境、测试环境)下,生效的变量,在本环境内唯一。
设置:
代码设置:pm.environment.set("环境变量名", 环境变量值)
// 示例
pm.environment.set("env_age", 99)
获取:
代码获取:var 接收值的变量 = pm.environment.get("环境变量名")
// 示例
var ret = pm.environment.get("env_age") // ret 的值为 99
请求参数获取(postman界面获取):{{环境变量名}}
// 示例
{{env_age}}
假设,这样一种场景:
调某接口时,要输入 “时间戳”,如果输入的 “时间戳” 的绝对值,超过标准时间10分钟。 则不允许调用。
一天:86400秒
51年 6月(180) 9天 — 1,624,665,600 秒
调用百度首页接口,传时间戳给服务器
实现步骤:
在 Pre-request Script 标签页中,添加代码。拿到时间戳,写入全局变量
// 拿到时间戳
var timestamp = new Date().getTime()
// 将时间戳设置到 全局变量
pm.globals.set("glb_timestamp", timestamp)
点击 Send 按钮,发送请求。请求发送前执行 上述代码。写入全局变量
**介绍:**应用于 多个 http请求之间,有数据关联、或依赖关系时。
A接口 依赖 B接口 的数据
请求 获取天气接口, 提取响应结果中的 城市, 将城市名,给百度搜索接口使用。
实现步骤:
在查询天气请求 Tests 中,编写代码 获取 城市名,写入全局变量
// 获取 全部响应结果
var jsonData = pm.response.json()
// 从响应结果中 获取 城市名
var city = jsonData.weatherinfo.city
// 将城市名写入到全局变量
pm.globals.set("glb_city", city)
修改 百度搜索请求,使用全局变量,按 城市名进行搜索。
http://www.baidu.com/s?wd={{glb_city}}
步骤:
批量执行测试用例集。(确认无误)
导出 Export 用例集。(得到 xxxx.json文件)
在 终端 中 执行命令,生成测试报告
# 现在 终端中,测试一下。
newman run xxxx.json
# 完整的命令
newman run xxxx.json -e 环境变量文件 -d 外部数据文件 -r html --reporter-html-export 测试报告名.html
# 示例:
newman run 批量执行测试用例.postman_collection.json -r html --reporter-html-export 我的第一个测试报告.html
# 如果添加 -r html 就报错!说明: newman-reporter-html 安装失败!
使用Postman实现iHRM系统登录模块和员工管理模块接口测试
当 http请求,使用的 数据有较高相似度,相同的请求时,考虑使用参数化(将数据组织到数据文件中)。
CSV:
JSON:
在 Postman 中,选中使用数据文件的 用例集,导入数据文件。
创建 xxx.json 数据文件
在 数据文件中,按json 语法写入json数据。postman要求,json格式的数据文件,数据内容的最外层,必须是 [ ] 。内部所有的数据用 {} 存储。( 建议:使用 json.cn 在线网站编写。)
在 Postman 中,选中使用数据文件的 用例集,导入数据文件。
根据使用数据 位置 不同,有两种获取数据文件中数据的方法。
需求:批量查询手机号归属地、所属运营商,校验运营商数据正确性
接口: http://cx.shouji.360.cn/phonearea.php?number=13012345678
测试数据:
手机号: 13012345678 运营商: 联通
手机号: 13800001111 运营商: 移动
手机号: 18966778899 运营商: 电信
替换 上述用例中的 两处:
强调:一旦替换完成,Send 就不再能正常获取 响应数据。(数据必须从数据文件中读取,才能正常发送请求,Send 不具备读取数据文件的功能)
小结:借助 数据文件,实现 “数据驱动”。—— 有多少条数据,对应就有多少个 http请求。
步骤:
批量执行测试用例(借助数据文件)
导出 测试用例集 (建议与 数据文件 放在一起)
执行生成测试报告的命令
newman run 用例集名称.json -d 数据文件名.csv/.json -r html --reporter-html-export 报告名称.html
以上的依赖关系,借助 postman关联 和 环境变量 来实现!!
发送 http 登录请求,在响应体中 得到 data值。
将 data值 拼接 “Bearer” 和 “空格” ,组成一个 合法令牌。
将 拼接后的令牌 环境变量中。(在 Tests 中通过代码写入)
// 拼接 data的值,添加 Bearer 和 空格。组成合法令牌
// 获取 响应体, 保存在 jsonData变量中。
var jsonData = pm.response.json()
// 提取 data的值。保存在 tmp_token 中。
var tmp_token = jsonData.data
// 拼接 Bearer 和 空格 到 data值的前面。 "Bearer" + " " + tmp_token
pm.environment.set("env_token", "Bearer"+" "+tmp_token)
获取 添加员工成功的 data下面的 id值。
将 员工id,设置 环境变量(代码写入 Tests 中)
// 获取添加员工的 响应体
var jsonData = pm.response.json()
// 提取员工id
var tmp_id = jsonData.data.id
// 设置到环境变量。
pm.environment.set("env_emp_id", tmp_id)
发送 Send 之前,注意先修改 手机号,保证手机号唯一!
点击 “眼睛” 图标 查看。
将测试用例中的,请求方法、URL、请求头,写入到 postman对应请求中。
给 :target 对应 的 路径参数,填充上 “员工id”的值。( {{员工id环境变量}} )
修改员工
删除员工
查询员工列表:
将 环境文件,与测试用例集文件,放置到同一个目录下。
执行命令:
newman run 测试用例集文件.json -e 环境文件.json -r html --reporter-html-export 测试报告名.html
# 示例:
newman run iHRM人力资源管理系统测试用例集.postman_collection.json -e 测试环境.postman_environment.json -r html --reporter-html-export iHRM的测试报告.html