接口测试全流程笔记
接口测试基础
什么是接口?
一个物体用于和外界连接的部分。汽车油箱口、手机的充电口....
计算机硬件:USB、HDMI
计算机软件:为了实现代码复用,或者说功能重复使用
本地调用:
代码单元间、模块间调用。比如:文件写入功能
如果一部分代码实现了一个功能模块--服务。比如:天气服务
如何提供给其他人调用?
RPC
Http --提供更便捷和更广泛的调用方式 **web前端调用
WebSocket --提供双向通信
Socket
底层TCP协议
*越是上层的调用方式,封装的越详细,调用越简单。但是效率越低
什么是Web系统三层架构?
接口:大部分是基于http协议的网络服务。
常见的接口都是那些类型?
1.按照调用关系划分
垂直调用:应用层与服务层传递数据的HTTP 接口
平行调用:服务层内部或者说服务于服务之间的接口(http、rpc)
系统级别调用:提供给第三方系统(用户)调用的接口(http、rpc、WebSocket)
比如:淘宝开发api、气象局的天气预报接口、中国航空信息公司提供的航班信息服务、第三方登录接口
2.按技术实现来划分
WebService ---网络服务架构
SOAP WebService
请求和响应的正文内容都是 有标准格式的xml
接口的所有参数配置和信息描述都在一个xml文件里--WSDL
有严格的规范和标准,包括安全,事务等各个方面的内容
也要借助于各种网络协议传输(最常见 绑定http协议)
优点:
规范、统一
对于 对稳定性和准确性要求比较高的业务团队还是适用的
缺点:复杂、笨重
Restful API
网络上的所有事物都可以被抽象为资源(resource)
每一个资源都有唯一的标识符:URI(统一资源定位符)指向它
HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE,改变资源的状态
优点:轻量、简化开发
缺点:
随意性比较强、规范性不好
安全性、稳定性、性能需求依赖于后期优化
RPC
什么是RPC?
远程过程调用:他是一个技术概念,不是一个协议
举例:如果你想解决一顿午饭
方式一:
在百度上查询商家,每个商家都有订餐电话。每个商家也有地址
去店里点餐,或者打电话过去订餐
方式二:
在饿了吗搜索能够提供午餐的商家。
在饿了吗平台上下单-->饿了吗将订单转发给商家-->商家为你提供 备餐或送餐服务
优势:
对于消费者:
你不需要知道商家电话或地址
直接在平台远程调用服务
同样是提供麻辣烫的商户,如果休息了你可以选择提供同样服务的商户
对于商家:
只需要接入饿了吗平台,专心提供服务就好
动态上下线很方便
方式一:就是传统的http协议
方式二:就是典型的rpc框架
对比:
如果在大型系统的内部,比如电商就是这样,单独有一个订单系统,支付系统,商品系统,用户系统。都是分开部署,单独上线的
系统A的代码,想调用一个内部服务B的某个函数(服务)?
1.直接使用http协议
优点:
简单、直接、开发方便;接口不多、系统与系统交互较少的情况下使用
缺点:
1.必须有明确的请求地址,携带完整的参数调用(包括头)
2.要单独添加监控机制或负载均衡
3.一旦请求失败,即使有失败重连的机制,也会造成不好的用户体验
4.动态扩展需要增加配置
2.使用RPC框架 --分布式架构的解决方案
优点:
1.无感知调用。我只认识服务,像调用一个远程函数一样方便
2.长链接,不必每次通信都要像http 一样去3次握手,减少了网络开销
3.RPC框架一般都有注册中心,有丰富的监控管理;发布、下线接口、动态扩展等
4.有较为健全的负载均衡和容错机制
5.安全性高
6.测试起来也比较直观
dubbo又是什么?
dubbo只是众多RPC思想实现的框架之一,阿里研发,后开源。现在逐渐被阿里换代成:HSF、SOFA
用户:当当、京东、去哪儿、汽车之家....
其他框架:Hessian、google的grpc
核心架构:
注册中心:广播、zookeeper、redis、dubbo(自带)
监控中心
服务提供方 Provider
服务消费方 Consumer
Dubbo环境准备:
*安装注册中心:zookeeper
安装简单监控中心:simple-monitor
*web容器:提供访问 jetty、spring、tomcat
dubbo管理后台:dubbo-admin
*项目
api --定义接口信息
provider --接口逻辑的实现部分
依赖api项目
启动spring容器
运行后 向注册中心注册服务
consumer --调用者
如何测试dubbo接口
创建java消费者调用
*jmeter
*python调用
*hessian+http
0.hessian是一种二进制序列化的方式。请求和响应的参数都是二进制形式
1.dubbo项目要配置使用hessian方式序列化
2.安装一个第三方模块,python-hessian
pip install python-hessian
3.通过接口文档或者注册中心后台管系统查询,得到接口地址(主机地址+接口名),方法名
4.代码
from pyhessian.client import HessianProxy
url = 'http://地址+接口名'
service = HessianProxy(url)
res = service.方法名(参数列表)
*telnet
xml-rpc
简单的理解是:将数据定义为xml格式,通过http协议进行远程传输
类库:xmlrpclib --标准库
import xmlrpclib
server = xmlrpclib.Server('http://地址+接口名')
server.方法名(参数列表)
WebSocket
什么websocket协议
html5中的一个新协议(应用层),使得浏览器具备了实时双向通信的能力
借用了HTTP的协议来完成一部分握手
请求:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
Sec-WebSocket-Key 是一个 Base64 encode 的值,这个是浏览器随机生成的
Sec_WebSocket-Protocol 是一个用户定义的字符串,用来区分同URL下,不同的服务所需要的协议
响应:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Accept 这个则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key
为什么要使用websocket
因为http协议的无状态性和单向性,如果要等待服务器的通知,传统方式要轮询服务器。造成资源浪费
客户端:有没有新信息(Request)
服务端:没有(Response)
客户端:有没有新信息(Request)
服务端:没有。。(Response)
客户端:有没有新信息(Request)
服务端:你好烦啊,没有啊。。(Response)
使用websocket;
客户端:我要建立Websocket协议,需要的服务:chat,Websocket协议版本:17(HTTP Request)
服务端:ok,确认,已升级为Websocket协议(HTTP Protocols Switched)
客户端:麻烦你有信息的时候推送给我噢。。
服务端:ok,有的时候会告诉你的
.....
服务端:你好,有新的消息
只需要经过一次HTTP请求,就可以做到源源不断的信息传送了(异步请求或者回调都是这个原理)
如何测试websocket接口
jmeter
python
第三方库 websocket-client : https://pypi.org/project/websocket-client/
pip install websocket-client
import websocket
ws = websocket.create_connection('ws://xxx.xxx.xx')
ws.send('xxxx')
print ws.recv()
http协议基础
HTTP请求结构
请求行
请求地址、协议的版本、请求方法名
请求报文头:以明文的字符串格式传送,以冒号分隔的键/值对
请求头部通知服务器有关于客户端请求的信息
User-Agent、 Accept、 Accept-Encoding、 Content-Type、Cookie
请求正文:数据内容
四种格式:
1. application/x-www-form-urlencoded
对数据进行序列化处理,以键值对形式 key1=value1&key2=value2的方式发送到服务器
2. multipart/form-data,
将表单中数据全部上传,包括文件
3.字符串文本格式:raw
text/plain 纯文本。浏览器不解析
text/html html。浏览器自动解析
text/xml 或 application/xml 后者可指定编码格式
application/json:消息主体是序列化后的 JSON 字符串
4.二进制格式:binary
HTTP响应结构
响应行
HTTP-Version表示服务器HTTP协议的版本
Status-Code表示服务器发回的响应状态代码 1 2 3 4 5
响应报文头:以明文的字符串格式传送,以冒号分隔的键/值对
响应头部通知客户端有关于服务端的应答信息
Server、 Content-Type ... Cookie
响应正文:待测试的数据
html --文本检索、样式内容浏览器检查
xml、json –解析后获取关键数据
***实践:利用Postman测试,四种常规格式的http接口
1.get请求 无参数
2.get 有参数
3.post 请求
正文格式:
1.x-www-form-urlencoded
2.soap xml
3.json
4.form-data
什么是接口测试?
测试对象:接口
测试流程:输入数据--->发送请求--->获取响应--->检查响应
重点通过不同的输入数据组合,检查数据的传输:
*通过接口测试的也是功能(代码逻辑实现)
测试业务逻辑
测试数据库读写
覆盖代码分支
性能
安全
进行接口测试的意义?
1.重视服务端的测试
服务端提供的一个个接口服务,也是产品。需要进行测试
2.提供整体研发团队的效率和质量 --前端未动,接口测试先行
a.测试早介入,防止资源闲置
b.协助前后端联调
c.如果服务端做修改,直接测试,效率高
d.实时监控
3.安全防范
隐藏域攻击、篡改关键参数其实都是想绕过前端,攻击接口
a.对于接口参数传递
格式、非空、长度 有没有基本的容错能力(或者说是参数限制)
b.接口返回的字段中是否包含多余信息(比如用户id,token等敏感字段)
c.用户密码、其他用户隐私信息传输时都需要进行加密后传输
d.登录状态 或者说对于cookie和session的使用也是测试重点
e.接口是否存在防刷限制
f.xss攻击/sql注入
4、性能测试
a 压力测试;
b 稳定性测试;
5.测试难度小,运行稳定,覆盖率直观
如何进行接口测试?
测试方法上:
怎么设计接口测试用例?依据:API文档
a.类似于黑盒功能测试,区别在于:通过参数组合覆盖代码逻辑
理论上来说,对于单个接口要覆盖到所有正常和异常分支
等价类划分、边界值划分、错误推算
b. 性能测试:压力测试、稳定性测试
c.接口安全测试: 加密规则、策略性限制、 xss攻击/sql注入
对于响应结果如何验证
a.验证接口响应状态码 200
b.响应报文是否包含关键信息 比如userid=10
c.响应报文关键字段是否存在
d.响应报文关键字段,值是否正确
格式-->调用其余接口查询-->查询数据库
测试策略上:
新项目/新功能 –完整的测试接口功能
已有功能 --基本功能要做到尽量覆盖(可以分批实现,比如先拉取线上10%的接口做监控)
首先要达到100%的单接口覆盖率
在有条件的情况下做到接口组合的业务逻辑覆盖
尽量帮助技术团队基线化、优化接口文档
技术选型:
浏览器插件: poster、 postman
典型商业工具: loadrunner、soapui
典型开源工具: jmeter、 RobotFramework、httprunner
开发辅助类库: httpclient(okhttp)、 requests
自动化可行方案推荐:
1.postman+newman+jenkins
2.jmeter+maven+jenkins
3.RobotFramework+jenkins或httprunner
4.利用httpclient或requests自研发工具
接口自动化测试的优势?
在三层金字塔结构中,接口自动化测试优势:
开发简单
运行稳定(误报率低)、测试效率高(速度高、bug优先级高)、利于监控
脚本维护量小
接口自动化测试工具篇--jmeter
接口自动化测试工具篇--postman
75ff30521dd7bafb48e07cf7e0a0b564dd8896a4para=address=汤里路220号院007&time=2018-12-31 14:00:00&title=慧测自动化大会
基于Python的接口自动化测试
requests是python的一个HTTP客户端库,跟urllib,urllib2类似,那为什么要用requests而不用urllib2呢?官方文档中是这样说明的:
“python的标准库urllib2提供了大部分需要的HTTP功能,但是API太逆天了,一个简单的功能就需要一大堆代码。”
1. 安装
https://pypi.python.org/pypi/requests
api:http://cn.python-requests.org/zh_CN/latest/
pip install requests
2.import requests
3.定制请求头
headers 字典
url = 'https://api.github.com/some/endpoint'
headers = {'user-agent': 'my-app/0.0.1'}
requests.get(url, headers=headers)
4.发送请求
a.无参数的get请求
b.有参数的get请求
c.无参数的post请求
d.有正文体的post请求
1).x-www-form-urlencoded形式
headers增加配置 Content-Type:application/x-www-form-urlencoded
data传入字典
2).表单提交
data传入字典
payload = {'key1': 'value1', 'key2': 'value2'}
requests.post("http://httpbin.org/post", data=payload)
如果有文件:
files = {'myfile': open('report.xls', 'rb')}
requests.post(url, files=files)
3).raw(文本)提交
字符串形式\xml形式
payload = '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" '
r = requests.post(url, data=payload)
json形式
方式一:字典转字符串
payload = {'some': 'data'}
r = requests.post(url, data=json.dumps(payload))
方式二:json字典
payload = {'some': 'data'}
r = requests.post(url, json=payload)
4).二进制文件
files = {'file': open('report.xls', 'rb')}
requests.post(url, files=files)
5.添加Cookie
headers 添加
headers = {'cookie': 'token=xxx;uid=1'}
cookies 字典添加
cookies = dict(cookies_are='working')
cookies ={'cookies_are':'working'}
requests.get(url, cookies=cookies)
6.设置超时
requests.get('http://github.com', timeout=0.001)
timeout 仅对连接过程有效,与响应体的下载无关
超时则抛出requests.exceptions.Timeout
7.接收请求
requests的get和post方法返回值都是Response对象,其存储了服务器响应的内容
响应状态码:response.status_code
response.raise_for_status() #失败请求(非200响应)抛出异常
响应头:response.headers--字典
响应时间:elapsed
response.elapsed
response.elapsed.total_seconds()
response.elapsed.seconds
response.microseconds
Cookie: response.cookies--字典
响应体:response.text
内容编码格式问题
Requests 会基于 HTTP 响应头对响应的编码作出有根据的推测
response.encoding = 'utf-8' 指定编码格式,之后再调用response.text显示的编码格式就是设置后的
解析响应
1.json格式
response.json() --响应字符串转json对象(字典)
2.xml格式
python解析xml方式
使用ElementTree
1.加载xml--加载XML文件共有2种方法,一是加载指定字符串,二是加载指定文件
from xml.etree import ElementTree
root = ElementTree.fromstring(text)
root = ElementTree.parse("D:/test.xml")
2.获取对象
find方法 (支持部分xpath语法)
findall方法 (支持部分xpath语法)
注意命名空间:节点tag名称之前添加 {xxxx}
3.获取值、属性
.text
.attrib['category']
3.文件流 --响应中的字节内容(二进制):response.content
with open('./user.csv', 'wb') as file:
# 每128个流遍历一次
for data in res.iter_content(128):
# 把流写入到文件
file.write(data)
单元测试框架的使用
三个概念
Test Case
一个测试用例就是一个完整的测试单元,包含初始和收尾工作
基本单元为函数
Test Suite
一个功能往往有多个测试用例验证,用例的集合称为测试套件
Test Runner
测试用例执行的基本单元
用例组织
方式一:函数是用例,集中在一个类中
继承unittest.TestCase
每个用例都是一个无参的成员方法
方法名为test开头
名为runTest
主函数中调用unitest.main()函数 运行类中以test开头的所有测试用例
方式二:构造测试集合
unittest.TestSuite()
.addTest(类名("方法名"))
类需要事先import
unittest.TextTestRunner().run(suite)
创建run.py
方式三:使用discover
TestLoader
通过不同的方式加载测试用例,返回测试套件对象
defaultTestLoader
discover()
start_dir
测试用例所在目录
pattern
用例文件名匹配规则
使用fixture
setUpModule/tearDownModule
在整个模块开始或结束时被执行
普通函数
setUpClass/tearDownClass
在测试用例所在的类开始或结束时被执行
类方法
setUp/tearDown
在测试用例开始或结束时被执行
类的成员方法
断言使用
用例执行结果
成功 没有未处理的异常 and 一个或多个断言全部成功
失败 抛出任何未知的或未捕获的异常 or 至少一个断言失败
unittest的原始断言
unittest.TestCase类的成员方法
assertEqual(a, b)判断a==b
assertNotEqual(a, b)判断a != b
assertIs(a, b) a is b
assertIsNot(a, b) a is not b
assertIsNone(x) x is None
assertIsNotNone(x) x is not None
assertIn(a, b)a in b
assertNotIn(a, b) a not in b
assertIsInstance(a, b) isinstance(a, b)
assertNotIsInstance(a, b) not isinstance(a, b)
生成测试报告
HTMLTestRunnerCN https://github.com/findyou/HTMLTestRunnerCN
使用方法:
1.下载文件,放在Lib路径下 import HtmlTestRunnerCN
2.创建 HTMLTestRunnerCN对象
HTMLTestRunnerCN.HTMLTestRunner(stream=,title=,description=)
stream文件流 fp = open(path, 'wb')
3.将测试运行方式与原来的
unittest.TextTestRunner().run(suite)
改为:HTMLTestRunnerCN(xxx).run(suite)
4.编写函数注释(或类注释),显示用例描述
jenkins集成
接口自动化测试框架开发 --需求驱动开发
框架方式一:代码用例驱动
1.http客户端代码封装
a.引入client类 --目的:避免多次发送请求
实例化client,传入接口调用参数 url\type
type和data-type使用枚举类
from enum import Enum, unique
@unique
class Weekday(Enum):
Sun = 0
Mon = 1
.......
调用send方法,请求接口。响应保存在实例变量content中
调用验证点模块直接判断结果
2. 检查点添加
*普通检查点
check_status_code
check_equal
check_not_equal
check_gt
check_lt
check_contains
check_startswith
引入jsonpath https://pypi.python.org/pypi/jsonpath/
json_value
json_values
json_values_count
xml_value
xml_values
transmit
*数据库检查点
python操作mysql数据库
mysqldb --停止更新,不支持3
pymysql --推荐
https://pypi.org/project/PyMySQL/
安装:pip3 install PyMySQL 或源码安装
使用:
1.创建连接
db = pymysql.connect(host="139.199.132.220", user="root", password="123456", db="event")
2.创建游标
cursor = db.cursor()
3.执行语句
cursor.execute("xxx")
4.获取执行结果
cursor.fetchone()
cursor.fetchall()
5.关闭数据库连接
db.close()
6.修改数据:db.commit()
封装检查点函数
查询类、清理数据类
3.利用unittest配置函数,简化用例编写流程
4.常用业务工具函数封装
用户密码转码,数字签名生成
5.解决关联接口调用
a.方案一利用全局字典
检查点函数中新增 值传递方法 transmit
6.编写用例运行文件 run.xml
解析xml文件
python动态引入package
importlib.import_module('包路径') 返回package对象
动态执行代码
FUNC_TEMPLATE = '''def {test_id}(self):
'{desc}'
ITest.execute_case({data})
'''
exec(FUNC_TEMPLATE.format(test_id='test_'+case.name, desc=case.desc, data=case_id))
7.数据驱动 ddt
安装:pip install ddt
import ddt
类加注解:@ddt.ddt
方法加注解:@ddt.data
使用ddt后,会产生一个新的测试用例方法名:之前的测试用例方法名_ordinal_data
方式一:data直接放入数值
@data(3, 4, 12, 23)
def test_add(self, value):
print(value)
方式二:data放入复杂的数据结构
test_data = [
{
"username": "huice",
"password": "MTIzaHVpY2VodWljZSFAIw==",
"assert": 0
},
{
"username": "huice",
"password": "aHVpY2VodWljZSFAIw==",
"assert": 10000
},
{
"username": "",
"password": "MTIzaHVpY2VodWljZSFAIw==",
"assert": 10001
},
{
"username": "huice",
"password": "",
"assert": 10001
}]
@data(*test_data)
def test_add(self, value):
print(value)
框架方式二:excel用例驱动
0.规定excel模板
1.python读取excel--xlrd
https://pypi.org/project/xlrd/
pip install xlrd
1、常用单元格中的数据类型
0. empty(空的),1 string(text), 2 number, 3 date, 4 boolean, 5 error, 6 blank(空白表格)
2、导入模块 import xlrd
3、打开Excel文件读取数据
data = xlrd.open_workbook(filename)
4、常用的函数
excel中最重要的方法就是book和sheet的操作
1) 获取book中一个工作表
table = data.sheets()[0] #通过索引顺序获取
table = data.sheet_by_index(sheet_indx)) #通过索引顺序获取
table = data.sheet_by_name(sheet_name)#通过名称获取
以上三个函数都会返回一个xlrd.sheet.Sheet()对象
names = data.sheet_names() #返回book中所有工作表的名字
data.sheet_loaded(sheet_name or indx) # 检查某个sheet是否导入完毕
2) 行的操作
nrows = table.nrows #获取该sheet中的有效行数
table.row(rowx) #返回由该行中所有的单元格对象组成的列表
table.row_values(rowx, start_colx=0, end_colx=None) #返回由该行中所有单元格的数据组成的列表
table.row_len(rowx) #返回该列的有效单元格长度
3)列(colnum)的操作
ncols = table.ncols #获取列表的有效列数
table.col(colx, start_rowx=0, end_rowx=None) #返回由该列中所有的单元格对象组成的列表
table.col_values(colx, start_rowx=0, end_rowx=None) #返回由该列中所有单元格的数据组成的列表
4)单元格的操作
table.cell(rowx,colx) #返回单元格对象
table.cell_type(rowx,colx) #返回单元格中的数据类型
table.cell_value(rowx,colx) #返回单元格中的数据
2.拼接代码
3.解决全局变量
4.完善检查点函数和值传递
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)