* 接口:不同的系统之间相互连接的部分,是一个传递数据的通道
* 接口测试:检查数据的交换、传递和控制管理过程
网络模型:
OSI七层模型
应用层
表示层
会话层
传输层
网络层
数据链路层
物理层
TCP/IP四层模型
应用层
传输层
网络层
网络接口层
TCP/IP协议栈
DNS域名解析
TELNET远程终端协议
HTTP超文本传输协议
FTP文件传输协议
SMTP发邮件协议
POP3收邮件协议
TCP/UDP
IP、ARP、RARP、ICMP、IGMP
各种通讯网络接口(以太网等)(物理网络)
应用层
传输层(段)
网络层(包)
数据链路层(帧)
物理层(比特流)
常见接口协议:
Network Access LAN connection WAN connection
Internet IP
Transport TCP UDP
Application FTP HTTP SMTP email DNS TFTP
RPC协议:以本地代码调用的方式实现远程执行
主要用于公司内部的服务调用
常见的RPC协议:
Dubbo
gRPc
THrift
> HTTP协议
请求:
请求行:method url protocol
请求方法:GET、POST、PUT、DELETE、HEAD
请求头:Host Cookies User-Agent
请求参数: query
请求体:JSON XML FORM
响应:
响应状态行
响应头
响应体
URI和URL
* URI,统一资源标识符,用来唯一的标识一个资源。
* URL,统一资源定位符,它是一种具体的 URI
* URL 结构:`https://www.baidu.com/s?wd=霍格沃兹&rsv_spt=1`
1. 协议:`http`
2. 域名:`www.baidu.com`
3. 端口:跟在域名后面,域名和端口之间使用“:”作为分隔符,非必须,如果省略端口部分将采用默认端口
4. 路径:`/s`
5. 请求参数:`wd=霍格沃兹&rsv_spt=**1`
HTTP响应状态码:
* 1xx 临时响应,表示通知信息,请求收到了或正在进行处理
* 2xx 表示成功,接受或知道了
* 200 成功
* 3xx 表示重定向,要完成请求还必须才去进一步的行动
* 301 永久移动
* 302 临时移动
* 4xx 表示客户端请求错误
* 403 未授权
* 404 未找到
* 5xx 表示服务端错误
* 500 服务器内部错误
* 503 服务不可用
## RESTful 架构
* Restful:Representational State Transfer
* 借助于 HTTP 协议的基本请求方法代表资源的状态切换
* GET:获取资源
* POST:新增或者更新
* PUT:更新资源
* DELETE:删除资源
# 接口测试用例设计
## 接口测试流程
需求分析-测试设计-测试用例评审-测试执行-验收-预发布-上线
接口测试思路:
接口幂等性-重复提交
故障注入-Redis故障降级测试
故障注入-服务故障转移测试
线程安全测试-并发测试
线程安全测试-分布式测试
线程安全测试-数据库读写安全测试
基本功能流程测试-冒烟测试
基本功能流程测试-正常流程覆盖测试
基于输入域的测试-边界值测试
基于输入域的测试-特殊字符校验
基于输入域的测试-参数类型校验
基于输入域的测试-必选参数校验
基于输入域的测试-组合参数校验
基于输入域的测试-有效性校验
基于输入域的测试-默认值校验
基于输入域的测试-排重逻辑
## Postman
* 官网下载地址
* https://www.postman.com/downloads
##Postman使用
#发送 GET 请求
* 新建请求
* 填写请求方式:GET
* 填写请求 URL:
* https://ceshiren.com/
* https://httpbin.ceshiren.com/get
* 填写请求参数: para_key = para_value
#发送POST请求
* 请求方式:POST
* 请求 URL:https://httpbin.ceshiren.com/post
* 请求参数
* FORM 格式:Body –> form-data
* JSON 格式:Body –> raw –> JSON
* 文件格式:Body –> form-data –> File
## 查看接口响应
* 响应体
* 响应头
* 响应状态
## 操作头信息
* 添加请求头
* 修改请求头
## 宠物商店接口文档分析
* 接口文档:https://petstore.swagger.io
* 宠物的增删改查
## 接口测试用例设计
* 宠物的增删改查冒烟测试用例
## Postman 完成接口测试
## 运行测试集
* 测试集页面 -> Run 按钮
* 创建测试集
* 编写断言
* 运行测试集
* 查看测试结果
## 断言
* 验证响应状态码
* 验证响应体中是否包含某个字符串
* 验证 JSON 中的某个值是否等于预期的值
* 验证响应体是否与某个字符串完全相同
* 验证响应头信息中的 Content-Type 是否存在
* 验证响应时间是否小于某个值
// Status Code:Code is 200
// 验证响应状态码
pm.test("响应状态码为 200", function () {
pm.response.to.have.status(200);
});
// Response Body:contains string
// 验证响应体中是否包含某个字符串
pm.test("响应体中包含预期的字符串", function () {
pm.expect(pm.response.text()).to.include("doggie");
});
// Response Body:JSON value check
// 验证 JSON 中的某个值是否等于预期的值
pm.test("宠物名称为 doggie", function () {
var jsonData = pm.response.json();
pm.expect(jsonData[0].name).to.eql("doggie");
});
// Response Body:Is equal to a string
// 验证响应体是否与某个字符串完全相同
pm.test("响应体正确", function () {
pm.response.to.have.body("response_body_string");
});
// Response Body:Content-Type header check
// 验证响应头信息中的 Content-Type 是否存在
pm.test("Content-Type is present", function () {
pm.response.to.have.header("Content-Type");
});
// Response time is less than 200ms
// 验证响应时间是否小于某个值
pm.test("Response time is less than 200ms", function () {
pm.expect(pm.response.responseTime).to.be.below(200);
});
## 变量
* Postman 中变量的种类与作用域
* Data:在测试集中上传的数据
* Environment:环境范围
* Collection:集合范围
* Global:全局范围
* Local:在脚本中设置的变量
## 变量定义
* 全局变量:Environments -> Globals
* 测试集变量:测试集页面 -> Variables
* 环境变量:Environments -> +
## 变量的使用
* 请求 URL, Params 参数或 Body 表格或JSON/XML 文本中通过 `{{变量名}}` 使用
* 在 Pre-request Script 和 Tests 脚本中使用封装好的语句获取或者设置对应变量
```
// 获取全局变量
var status = pm.globals.get("status");
// 输入到控制台
console.log(status)
// 获取测试集变量
var petId = pm.collectionVariables.get("petId");
// 获取环境变量
var url = pm.environment.get("baseURL");
// 设置全局变量
pm.globals.set("status", "sold");
// 设置测试集变量
pm.collectionVariables.set("petId", 0);
// 设置环境变量
pm.environment.set("baseURL", "");
```
## 变量的优先级
* 优先级从高至低为:Data -> Enviroment -> Collection -> Global -> Local
* 验证响应状态码
* 验证响应体中是否包含某个字符串
* 验证 JSON 中的某个值是否等于预期的值
* 验证响应体是否与某个字符串完全相同
* 验证响应头信息中的 Content-Type 是否存在
* 验证响应时间是否小于某个值
```
// Status Code:Code is 200
// 验证响应状态码
pm.test("响应状态码为 200", function () {
pm.response.to.have.status(200);
});
// Response Body:contains string
// 验证响应体中是否包含某个字符串
pm.test("响应体中包含预期的字符串", function () {
pm.expect(pm.response.text()).to.include("doggie");
});
// Response Body:JSON value check
// 验证 JSON 中的某个值是否等于预期的值
pm.test("宠物名称为 doggie", function () {
var jsonData = pm.response.json();
pm.expect(jsonData[0].name).to.eql("doggie");
});
// Response Body:Is equal to a string
// 验证响应体是否与某个字符串完全相同
pm.test("响应体正确", function () {
pm.response.to.have.body("response_body_string");
});
// Response Body:Content-Type header check
// 验证响应头信息中的 Content-Type 是否存在
pm.test("Content-Type is present", function () {
pm.response.to.have.header("Content-Type");
});
// Response time is less than 200ms
// 验证响应时间是否小于某个值
pm.test("Response time is less than 200ms", function () {
pm.expect(pm.response.responseTime).to.be.below(200);
});
```
## 运行测试集
* 测试集页面 -> Run 按钮

## 查看测试结果

## 变量
* Postman 中变量的种类与作用域
* Data:在测试集中上传的数据
* Environment:环境范围
* Collection:集合范围
* Global:全局范围
* Local:在脚本中设置的变量
## 变量定义
* 全局变量:Environments -> Globals
* 测试集变量:测试集页面 -> Variables
* 环境变量:Environments -> +
## 变量的使用
* 请求 URL, Params 参数或 Body 表格或JSON/XML 文本中通过 `{{变量名}}` 使用
* 在 Pre-request Script 和 Tests 脚本中使用封装好的语句获取或者设置对应变量
```
// 获取全局变量
var status = pm.globals.get("status");
// 输入到控制台
console.log(status)
// 获取测试集变量
var petId = pm.collectionVariables.get("petId");
// 获取环境变量
var url = pm.environment.get("baseURL");
// 设置全局变量
pm.globals.set("status", "sold");
// 设置测试集变量
pm.collectionVariables.set("petId", 0);
// 设置环境变量
pm.environment.set("baseURL", "");
```
## 变量的优先级
* 优先级从高至低为:Data -> Enviroment -> Collection -> Global -> Local
* 验证响应状态码
* 验证响应体中是否包含某个字符串
* 验证 JSON 中的某个值是否等于预期的值
* 验证响应体是否与某个字符串完全相同
* 验证响应头信息中的 Content-Type 是否存在
* 验证响应时间是否小于某个值
// Status Code:Code is 200
// 验证响应状态码
pm.test("响应状态码为 200", function () {
pm.response.to.have.status(200);
});
// Response Body:contains string
// 验证响应体中是否包含某个字符串
pm.test("响应体中包含预期的字符串", function () {
pm.expect(pm.response.text()).to.include("doggie");
});
// Response Body:JSON value check
// 验证 JSON 中的某个值是否等于预期的值
pm.test("宠物名称为 doggie", function () {
var jsonData = pm.response.json();
pm.expect(jsonData[0].name).to.eql("doggie");
});
// Response Body:Is equal to a string
// 验证响应体是否与某个字符串完全相同
pm.test("响应体正确", function () {
pm.response.to.have.body("response_body_string");
});
// Response Body:Content-Type header check
// 验证响应头信息中的 Content-Type 是否存在
pm.test("Content-Type is present", function () {
pm.response.to.have.header("Content-Type");
});
// Response time is less than 200ms
// 验证响应时间是否小于某个值
pm.test("Response time is less than 200ms", function () {
pm.expect(pm.response.responseTime).to.be.below(200);
});
**Fiddler抓包:**
* 官网:https://www.telerik.com/fiddler/fiddler-classic
* Fiddler 是位于客户端和服务器端的 HTTP 代理
* 也是目前最常用的 HTTP 抓包工具之一
* 功能
* 监控流量
* 支持解密 HTTPS
* 查看分析接口数据
* 修改请求的数据
* 修改服务器返回的数据
* 设置断点
* 设置:Tools -> Options -> Connections
* 确认抓包端口:默认为 8888
* 默认勾选 Act as system proxy on startup

配置:
* Tools -> Fiddler Options -> HTTPS
* 勾选 Decrypt HTTPS Traffic
* 弹出安装证书弹窗 -> 选择【Yes】-> 点击 【Yes】安装证书
* 点击 Actions -> Open Windows Certificate Manager 查看证书
抓取移动端请求:
* Tools -> Fiddler Options -> Connections
* 勾选 Allow remote computers to connect
* 移动端与电脑处于同一网络下
* 移动端配置网络代理
* 代理服务器主机:电脑IP
* 代理服务器端口:8888
* 浏览器输入 http://电脑ip:8888 下载 Fiddler 证书进行安装
* Statistic:性能与统计数据
* Inspectors:请求和响应数据
* Filter:制定过滤规则
* Composer:编辑发出请求
## AutoResponder
* 拦截某一请求,并重定向到本地的资源,或者使用 Fiddler 的内置响应
* 用于调试服务器端代码而无需修改服务器端的代码和配置
#断点
* 菜单栏 -> Rules -> Automatic Breakpoints ->选择断点方式
* 状态栏点击
* 请求前断点:向上箭头
* 响应后断点:向下箭头
* 命令行下输入
* 请求前断点:`bpu xxx`
* 响应后断点:`bpafter xxx`
弱网:
* 菜单栏 -> Rules -> Performance -> Simulate Modem Speeds
* 修改网络配置参数
* 菜单栏 -> Rules -> Customize Rules…
* 搜索关键词 simulate
* 修改延迟时间
Postman参数传递:
* 创建登录接口
* 成功后在 Test 中提取 token
* 保存 token 到测试集变量
//获取响应数据的json对象
var jsonData = pm.response.json();
//从json对象中提取token值
var token = jsonData.data.token;
//把token值设置为测试集变量
pm.collectionVariables.set("token", token);
## 请求前脚本中获取参数
```
// 构造登录请求
const loginRequest = {
url: 'http://litemall.hogwarts.ceshiren.com/wx/auth/login',
method: "POST",
header: 'Content-Type: application/json',
body: {
mode: 'raw',
raw: JSON.stringify({'username': 'user123', "password": "user123"})
}
};
// 发送请求
pm.sendRequest(loginRequest, function (err, response) {
if (err) {
console.log(err);
} else {
console.log(response.json());
pm.collectionVariables.set("token", response.json().data.token);
}
});
```
弱网测试:
* 按照移动的特性,一般应用低于 3G、弱信号的 Wifi 可以划分为弱网
* 弱网测试是健壮性测试的重要部分,对移动端测试必不可少
* 弱网测试主要进行弱网状态下的功能测试,同时关注用户体验
包含:带宽、丢包、延迟
Mock测试场景:
* 前后端数据交互
* 第三方系统数据交互
* 硬件设备解耦
好处:
* 不依赖第三方数据
* 节省工作量
* 节省联调
* 匹配规则
* 模拟响应
## 协议分析工具
* 网络监听:TcpDump + WireShark
* 代理 Proxy
* 推荐工具:手工测试charles [全平台]、安全测试burpsuite [全平台 java]
* 自动化测试:mitmproxy
* 其他代理: fiddler [仅windows]、AnyProxy [全平台]
* 协议客户端工具: curl、postman
## tcpdump
* 参数:
* -x 十六进制展示
* -w file 保存文件
* 表达式:
* ip tcp 协议
* host 主机名
* port 80
* src 来源 dst 目的
* and or () 逻辑表达式
## WireShark
## 抓取访问百度的数据包
* `sudo tcpdump host www.baidu.com -w /tmp/tcpdump.log`
* `curl http://www.baidu.com`
* 停止tcpdump
* 使用wireshark打开/tmp/tcpdump.log
## [Test Double 测试替身](https://martinfowler.com/bliki/TestDouble.html)
* **Dummy 占位对象** 对象被传递但从未实际使用过。通常它们仅用于填充参数列表。
* **Fake 假对象** 对象实际上有工作实现,但通常采取一些捷径,这使得它们不适合生产(内存数据库就是一个很好的例子)。
* **Stubs 桩对象** 为测试期间调用提供预设答案,通常根本不响应任何超出测试程序的内容。
* **Spies 间谍对象** 它们还根据调用方式记录一些信息。其中一种形式可能是电子邮件服务,它记录发送了多少消息。
* **Mocks 模拟对象** 是我们在这里谈论的:预先编程的对象,这些期望形成了它们期望接收的调用的规范。
|概念|监听调用|预设期望|按需预设期望|真实数据|真实数据修改|
|---|---|---|---|---|---|
|Dummy 傀儡对象||||||
|Spy 间谍对象|✅|||||
|Fake 假对象|✅|||✅||
|Stub 桩对象|✅|✅||||
|Mock 模拟对象|✅|✅|✅|✅|✅|
## Mitmproxy 简介
* mitmproxy:交互式的命令行工具
* 注意:不支持 windows
* mitmweb:基于浏览器的界面交互工具
* mitmdump:简单的终端输出,可以编写强大的插件和脚本
> 官方网站:https://mitmproxy.org/
## Mitmproxy 安装
推荐使用 python 进行安装
```
// 方式一
pip install mitmproxy==5.2.0
// 方式二
pip install pipx
pipx install mitmproxy==5.2.0
// 验证是否安装成功
mitmdump --version
```
PC端证书配置:
1. 配置电脑代理
2. 启动 mitmproxy
3. 在浏览器输入地址 mitm.it
4. 选择对应系统下载证书并安装
## 移动端证书配置
1. 在手机配置代理,ip 配置为**电脑的 ip 地址**,端口配置为 mitmproxy 监听端口
2. 启动 mitmproxy
3. 在手机浏览器输入地址 mitm.it
4. 选择 Android,下载并安装,即可成功抓取手机端的 https 的数据包
工具:
* mitmproxy
* mitmweb
* mitmdump
## mitmdump 参数介绍
* `-p` 参数,指定监听端口,默认监听 8080
* `-s` 参数,执行 python 脚本
## 核心组件
* Addons(插件):https://docs.mitmproxy.org/archive/v5/addons-overview/
* Events(事件):https://docs.mitmproxy.org/archive/v5/addons-events/
## 脚本编写
```
# 创建 python 文件 request_demo.py
def request(flow):
flow.request.headers["myheader"] = "value"
```
```
// mitmdump 执行命令
mitmdump -p 8999 -s xx/request_demo.py
```
## 实现 MapLocal
1. 创建一个本地文件,设定响应数据
2. 编写脚本,在请求事件中,即给响应对象复制
3. 执行命令
4. 访问浏览器验证结果
```
from mitmproxy import http
# request 名称不能被改变
# mitmdump 加载这个脚本的时候
# 当有请求来的时候,就会回调这个request方法
# flow 为抓取到的请求信息
def request(flow: http.HTTPFlow) -> None:
# 发起请求,判断url是不是等于一个路径
if flow.request.pretty_url == "http://example.com/path":
# 一旦请求的url等于我们的预期,我们就会创造一个response
# 这个response比较简单,请求状态码为200,响应数据位hello world
# 响应数据格式为html
flow.response = http.HTTPResponse.make(
200, # (optional) status code
b"Hello World", # (optional) content
{"Content-Type": "text/html"} # (optional) headers
)
```
## 实现 Rewrite
1. 修改股票名称
2. 修改股票价格
```
from mitmproxy import http
# response 名称不能被改变
# mitmdump 加载这个脚本的时候
# 当拦截到响应之后,就会回调这个 response 方法
# flow 为抓取到的接口信息
def response(flow: http.HTTPFlow):
# 打印响应内容
print(flow.response.content)
```