unittest 框架
介绍
本质
做单元测试,是 Python 自带的框架
组成
TestCase(测试用例)
用来书写真正的用例代码
步骤
1. 导包
import unittest
2. 创建测试类
class 类名(unittest.TestCase):
pass
3. 书写测试方法
def test_方法名(self):
用例的代码
TestSuite(测试套件)
管理多个 TestCase(测试用例)
TestRunner(测试执行)
执行测试套件的
TestLoader(测试加载)
对 TestSuite 的补充,管理多个 TestCase(测试用例)
unittest.TestLoader().discover('用例的目录', '代码文件名*')
Fixture(测试夹具)
代码 写在 TestCase 代码中
方法级别
在每个⽤例执⾏前后都会⾃动调⽤, ⽅法名是固定
前置
def setUp(self):
pass
后置
def tearDown(self):
pass
类级别
在类中所有的测试⽅法执⾏前后 会⾃动执⾏的代码, 只执⾏⼀次
前置
@classmethod
def setUpClass(cls):
pass
后置
@classmethod
def tearDownClass(cls):
pass
断言
assertEqual(预期结果,实际结果)
判断预期结果和实际结果是否相等,如果相等, 用例通过,如果不相等,抛出异常, 用例不通过
assertIn(预期结果,实际结果)
判断预期结果是否包含在实际结果中, 如果存在,用例通过, 如果不存在,抛出异常,用例不通过
参数化
通过参数的方式来传递数据,从而实现数据和脚本分离。并且可以实现用例的重复执行。
unittest 测试框架,本身不支持参数化,但是可以通过安装unittest扩展插件 parameterized 来实现
pip install parameterized
使用
1. 导包 from parameterized import parameterize
2. 修改测试方法,将测试方法中的测试数据使用变量表示
3. 组织测试数据,格式 [(), (), ()], 一个元组就是一组测试数据
4. 参数化,在测试方法上方使用装饰器 @parameterized.expand(测试数据)
5. 运行(直接 TestCase 或者 使用 suite 运行)
测试报告
pip install HTMLTestReport 下载第三方报告模版
步骤
1. 导包 unittest、HTMLTestReport
2. 组装用例(套件, loader )
3. 使用 HTMLTestReport 中的 runner 执行套件
4. 查看报告
跳过
直接将测试函数标记成跳过
@unittest.skip('跳过的原因')
根据条件判断测试函数是否跳过
@unittest.skipIf(判断条件, reason='原因') # 判断条件为 True, 执行跳过
使用绝对路径
将来的项目是分目录书写的, 使用相对路径,可能会出现找不到文件的情况,此时需要使用绝对路径
方法
1. 在项目的根目录,创建一个 Python 文件(app.py 或者 config.py)
2. 在这个文件中获取项目的目录,在其他代码中使用路径拼接完成绝对路径的书写
获取当前文件的绝对路径,包括文件名
os.path.abspath(__file__)
获取文件路径的目录名称,不包括文件名
os.path.dirname(__file__)
接口测试
接口测试理论
接口
系统与系统之间数据交互的通道
接口测试
校验服务器回发的响应结果与预期结果是否一致
价值
- 可以发现页面测试发现不了的问题
- 符合 质量控制前移理念
- 低成本,高收益!
实现方式
工具
postman
jmeter
代码
python + requests + unittest
java + HttpClient
HTTP协议
简介
超文本传输协议,基于 请求和响应 应用层协议
特点
1. 客户端服务器模式
2. 简单快速
3. 灵活
4. 无连接
5. 无状态
Http请求
请求行
请求方法
get
查
没有请求体
post
增
put
改
delete
删除
没有请求体
URL
协议
http
https
IP(域名)
在网络环境中,唯一定位一台主机
端口
在主机上唯一定位一个应用程序
可以省略不写,跟随协议
http
80
https
443
资源路径
在 应用中,指定具体资源
可以省略不写
默认 值为 /
查询参数
给资源传参。多个查询参数间,使用 & 隔分,使用 k=v 格式
协议版本
http/1.1
请求头
数据组织格式 k:v
Content-Type
描述 请求体数据类型
取值
application/json
application/x-www-form-urlencoded
空行
代表请求头结束
请求体
携带给服务器的数据
数据类型受 Content-Type的值影响
不是所有请求都有请求体
post、put 有
Http响应
响应行
协议版本
http/1.1
响应状态码
1xx
指示性信息
2xx
成功
200 ok
3xx
重定向
4xx
客户端错误
404 Not Found
5xx
服务端错误
状态码描述
响应头
数据组织格式 k:v
空行
代表响应头结束
响应体
服务器回发给客户端的 数据
绝大多数响应包,都有响应体
从响应体,拿接口测试所需要的 实际结果
接口规范
传统风格
只用 get、post方法
url 不唯一
使用状态码用 200
RESTful风格
对应 http协议 ,使用 get、post、put、delete
url 唯一指定一个具体的数据资源
使用状态码灵活
接口测试流程
1. 需求分析
2. 接口文档解析
3. 设计接口测试用例
4. 准备接口测试脚本
工具
postman
代码
python + requests + unittest
5. 执行脚本,跟踪缺陷
6. 生成 接口测试报告
7. 接口自动化持续集成(可选)
接口文档解析
接口文档概念
API文档,后端开发工程师设计,描述 接口信息的文档
解析接口文档
请求
请求方法
URL
请求参数(请求数据)
请求头
请求体
响应
响应状态码
响应数据
响应体
预期结果
接口测试用例设计
接口测试维度(测试点)
功能测试
业务场景
性能测试
响应时⻓:从发送请求到接收到服务器回发响应包所经历的时间。
错误率:服务器运⾏出错的概率
吞吐量:服务器单位时间内,处理请求的数量
服务器资源利⽤率:cpu、内存、⽹络、磁盘等 硬件资源的占⽤率。
安全测试
攻击安全
业务安全
敏感数据是否加密
SQL注入
接口测试用例设计方法思路
单接口
正向
必须参数
组合参数
全部参数
反向
功能异常:数据格式正确,不能履⾏接⼝功能
数据异常:数据格式不正确
参数异常
多参
少参
无参
错误参数:参数名错误。
业务场景
尽量模拟用户实际使用场景
用较少的用例覆盖尽可能多的接口
一般情况下,覆盖正向测试即可
模版
接口文档解析
单接口测试用例
postman断言
概念
让 postman 代替 人工,自动判断预期结果与 实际结果是否一致。
常用断言
响应状态码
Status code: Code is 200
生成的代码中,包含的是 "预期结果"
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
响应体包含字符串
Response body: Contains string
pm.test("Body matches string", function () {
pm.expect(pm.response.text()).to.include("string_you_want_to_search");
});
只能判断,响应结果中,是否包含 指定字符串。
响应体json的数据值
Response body: JSON value Check
pm.test("Your test name", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.value).to.eql(100);
});
var jsonData = pm.response.json();
代表 json 响应结果所有内容
指定 json 结果中的一个 具体key 判断 value 值
断言工作原理
Tests 中的代码,在 http响应结果接收到 之后才执行。
postman关联
应用场景
接口之间有依赖关系
实现步骤
假设:A接口的数据,被B接口依赖
1. 发送 A接口请求,获取响应数据
var jsonData = pm.reponse.json()
2. 将 数据写入到 公共容器(全局变量、环境变量)中。
全局变量
在整个postman生效
pm.globals.set("全局变量名",全局变量值)
环境变量
在特定环境下生效
pm.environment.set("环境变量名", 环境变量值)
3. B接口,从 容器获取 数据, 发送接口请求
{{变量名}}
案例
查天气,获取城市,百度搜城市
登录,获取令牌,添加员工
Bearer单词不能拼错,首字母必须大写,只有一个空格
postman参数化
应用场景
同一个接口,使用不同的 测试数据。 使用参数,提高测试效率。
数据文件
csv
数据组织格式简单,适用于大量数据场景
缺点
1. 不能测试 bool 类型。因为 postman 读取 csv后,将所有非数值类型数据,自动添加 ”“ 变为字符串
2. 不能存储复杂数据类型(元组、列表、字典)
3. 不能实现 参数测试。
json
数据组织格式详细,适用于 少量、参数化测试。
优点:
1. 可以测试 bool类型
2. 能使用 复杂数据类型
3. 可以实现 参数测试。
缺点:相同数据量,json文件要远大于 csv 文件。
导入postman
1. 点用例集的 run 按钮
2. 进入 Runner 页面, 选择数据文件, 预览
读取数据文件中的数据
postman界面中(URL、请求头、请求体)
{{字段名}} 或 {{键名}}
代码中(Tests)
data.字段名 或 data.键名
postman生成测试报告
前提
给postman 安装 newman 插件
1. 先 安装 node.js
.msi 文件 ,双击 自动安装
查验 是否 安装成功
npm -v
2. 再 安装 newman
在线命令
npm install -g newman
查验 是否 安装成功
newman -v
3. 再 安装 newman的插件
npm install -g newman-reporter-htmlextra
完整命令
newman run 测试用例文件.json -e 环境文件.json -d 数据文件.json/.csv -r htmlextra --reporter-htmlextra-export 报告名.html
-e
没有使用环境变量,可以省略
-d
没有用到参数化(没使用数据文件), 可以省略
Requests库
简介
Requests库 是 Python编写的,基于urllib 的 HTTP库,使用方便。
安装
pip install requests
pip install requests -i https://pypi.douban.com/simple/
查验
pip 中查验
pip list
pip show requests
pycharm 中查验
file - settings - 项目名下的 python 解释器 - 列表中 能看到 requests
设置 http请求
resp = requests.请求方法(url='URL地址', params={k:v}, headers={k:v},
data={k:v}, json={k:v}, cookies='cookie数据'(如:令牌))
请求方法
get()
post()
put()
delete()
参数
url
string
params
查询参数
字典
headers
请求头
字典
data
表单类型 请求体
字典
json
json类型 请求体
字典
用户身份认证
token令牌
ihrm
cookie+session
tpshop
Session
简介
叫 会话。通常出现在网络通信中,从客户端借助访问终端登录上服务器,直到 退出登录 所产生的 通信数据,保存在 会话中。
特性
- Session 用于存储 用户的信息。
- Session 位于服务端。大小直接使用服务器存储空间
- Session 中的数据,不能随意被访问,安全性较高。
- Session 中存储的数据类型,受服务器影响,几乎能支持所有的数据类型。
session 可以自动管理cookie
案例6
tpshop完整登录
1. 创建 session实例
session = requests.Session()
2. session实例,调用 获取验证码方法
session.get(验证码url)
3. session实例,调用 登录方法
session.post(登录url、json请求体)
4. session实例,调用 用户订单详细方法
session.get(订单详细url)
获取指定响应数据
resp.status_code
响应状态码
resp.json()
json格式响应结果
JSONDecodeError
错误,说明响应结果不能转换为 json
Cookie
简介
工程师 针对 http协议是无连接、无状态特性,设计的 一种技术。 可以在浏览器端 存储用户的信息。
特性
- cookie 用于存储 用户临时的不敏感信息。
- cookie 位于浏览器(客户端)端。默认大小 4k(可以调整)
- cookie 中的数据,可以随意被访问,没有安全性可言。
- cookie 中存储的数据类型, 受浏览器限制。
cookie + Session 认证方式
图
案例5
演示代码,借助 cookie 实现 tpshop 完整登录
数据库
数据库操作应用场景
校验测试数据
请求发送后,响应结果中没有 数据库中变化的数据
构造测试数据
测试数据,使用一次就失效
测试接口前,不能确定数据是否存在
pymysql操作数据库
安装
pip install pymysql
操作步骤
1. 导包
2. 建立连接
3. 获取游标
4 执行SQL语句
查询语句
提取结果集数据
从结果集中提取一行记录:fetchone()
从结果集中提取size条记录:fechmany(size)
从结果集中提取所有记录:fechall()
rownumber
修改游标位置
增删改语句
成功
conn.commit()
失败
conn.rollback()
5. 关闭游标
6. 关闭连接
连接方法
connect()
conn = pymysql.connect(host="", port=端口,user="", password="", database="", charset="")
host
IP地址
port
端口号
int类型
user
用户名
password
密码
database
数据库名
charset
字符集
utf8
查询数据库
增删改
数据库工具类封装
类名 DBUtil
获取连接
私有方法
__get_conn()
如果 conn 为 空,创建连接
if conn is None
关闭连接
私有方法
__close_conn()
如果 conn 不为空, 关闭
if conn is not None
查询一条
增删改
当sql语句的字段和python中的字段相同时用反引号· · 代替
接口自动化测试框架
**api/: 存储接口对象层(自己封装的 接口)**
**scripts/: 存储测试脚本层 (unittest框架实现的 测试类、测试方法)**
**data/: 存储 .json 数据文件**
**report/: 存储 生成的 html 测试报告**
**common/: 存储 通用的 工具方法**
**config.py: 存储项目的配置信息(全局变量)**
**run_suite.py: 组装测试用例、生成测试报告的 代码**
Tpshop商城Login封装
1. 普通方式实现
2. 封装 接口对象层
指导思想
1. 将动态变化的 ,设计到 参数中
2. 将固定不变的, 写到方法的实现内
3. 响应结果,通过 return 返回
代码实现
类
获取验证码
参
session
登录方法
参
session
data
返回值
resp
3. 封装 测试脚本层
借助 unittest 框架, 添加测试类、测试方法
调用 自己封装的 接口,发送登录请求, 获取响应结果
根据响应结果,做断言
代码优化
setUpClass
创建 session实例
setUp
发送验证码请求
common_assert() 函数
通用断言函数
ihrm封装
封装登录
1. 普通方式实现
2. 封装 接口对象层
指导思想
1. 将动态变化的 ,设计到 参数中
2. 将固定不变的, 写到方法的实现内
3. 响应结果,通过 return 返回
代码实现
类
登录方法
参
json_data
返回值
resp
3. 封装 测试脚本层
借助 unittest 框架, 添加测试类、测试方法
调用 自己封装的 接口,发送登录请求, 获取响应结果
根据响应结果,做断言
代码优化
common_assert() 函数
通用断言函数
函数,存放到 单独 .py 文件
导包
Alt + Enter
import this name
此项目中,有同名的多个 函数/类/方法/模块
添加员工接口
1. 普通方式实现
2. 接口对象层
3. 测试用例层
4. 参数化(非必须)
步骤
1. 写数据到 json文件
[{},{},{}]
2. 读取json文件数据
[{},{},{}] --> [(),(),()]
3. 使用parameterized 实现参数化
1. 导包 from parameterized import parameterized
2. 在 通用测试方法上一行,添加 @parameterized.expand()
3. 给 expand() 传入 元组列表数据( 调用 自己封装的 读取 json 文件的 函数 read_json_data() )
4. 修改 通用测试方法形参,与 json 数据文件中的 key 一致。
5. 在 通用测试方法内,使用形参
通过操作数据库解决 手机号唯一问题
测试 添加员工 前, 删除 指定手机号
setUp
测试 添加员工
测试 添加员工 结束后, 再删除 指定手机号
tearDown
参数化
1. 将 测试数据,组织到 json 文件中
2. 从 json 文件中 读取 测试数据,转化成 元组列表
3. 借助 parameterized 实现参数化
1. 导包 from parameterized import parameterized
2. 在 通用测试方法上一行,添加 @parameterized.expand()
3. 给 expand() 传入 元组列表数据(调用自己封装的 转化 json 数成元组列表的函数)
4. 修改 通用测试方法,添加形参,个数、顺序 与 json 数据的 key 一致
5. 在 通用测试方法内, 使用形参
日志
什么是日志
log, 记录系统运行过程中产生的信息, 通常将信息,写入 xxx.log 文件中
为什么使用日志
查看系统运行状况
分析日志,定位 bug
日志级别
- logging.DEBUG:调试级别【高】
- logging.INFO:信息级别【次高】
- logging.WARNING:警告级别【中】
- logging.ERROR:错误级别【低】
- logging.CRITICAL:严重错误级别【极低】
日志使用
使用步骤
1. 初始化日志配置
日志文件名
日志间隔时间单位
单位的个数
日志文件保留个数
2. 导包,指定级别,打印日志
全量字段校验
概念
校验接⼝返回响应结果的全部字段(更进一步的断言)
校验内容
字段值
字段名
字段类型
校验流程
1. 编写 json 语法格式的 校验规则 schema
2. 准备校验数据
校验手段
在线工具校验
https://www.jsonschemavalidator.net
python代码校验
步骤
1. 导包 import jsonschema
2. 定义 jsonschema格式 数据校验规则
3. 调⽤ jsonschema.validate(instance="json数据", schema="jsonshema规则")
返回结果
校验通过
None
校验失败
SchemaError,规则语法错误
ValidationError,json数据不符合校验规则
完整结构
关键字
type
校验数据类型
integer
string
null
array
object
number
boolean
properties
辅助 type, 只有 type 为 objcet 时使用
进一步,给 object内的 字段,设定校验规则
required
指定 响应体中,必须存在的 字段名
const
指定 具体数据值
pattern
进行 string 的模糊匹配(正则表达式)
子主题 12