欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 新车 > 接口(interface) 测试

接口(interface) 测试

2025/4/19 0:03:01 来源:https://blog.csdn.net/HANG_WORLD/article/details/138676645  浏览:    关键词:接口(interface) 测试

前提

概念

接口:系统之间数据交互的通道。(本质是函数(方法))
接口测试,会绕过前端,直接对服务器进行测试

实现方式

软件:
postman:使用简单,上手难度低。功能较少。
jmeter:使用难度较大。上手难度大。功能齐全。
代码:
Python + requests + Unittest
java + HttpClient

http协议

HTTP:超文本传输协议,基于请求与响应的应用层协议
默认端口:80
https默认端口:443

特点:客户端/服务器模式、简单快速、灵活、无连接、无状态

URL:协议://ip地址:端口号/资源路径

请求:
请求行:请求方法 url 协议版本
请求头:键值对
。Content-Type:作用,指定 请求体的数据类型
请求体:数据类型受Content-Type影响

1xx 指示错误
200 成功
30x 重定向错误
403 访问被禁止( 权限不足)
404 请求资源不存在
500 服务器错误

接口风格

传统风格
在这里插入图片描述
restful风格
在这里插入图片描述

测试流程

需求分析 – 接口文档的解析 – 设计测试用例 – 脚本开发 – 缺陷跟踪 – 生成测试报告 – 接口自动化持续集成
在这里插入图片描述

接口文档解析:
接口文档:又称为API文档,是由后端开发编写,用来描述接口信息的文档。

测试维度

功能测试
。 单接口测试
。 业务场景功能测试
性能测试
。 响应时长:从发送请求到接收到服务器回发响应包所经历的时间。
。 错误率:服务器运行出错的概率
。 吞吐量:服务器单位时间内,处理请求的数量。
。 服务器资源利用率:cpu、内存、网络、磁盘等 硬件资源的占用率。
安全测试
。 攻击安全:木马,病毒…
。 业务安全:敏感数据加密存储、SQL注入…

测试用例

设计方法

在这里插入图片描述
单接口测试:
。 正向测试
。 反向测试

业务场景测试:
。 尽量模拟用户实际使用场景
。尽量用最少的用例,覆盖最多的接口请求
。 一般情况下,覆盖正向测试即可

编写

用例编号用例名称模块优先级前置条件接口名称请求方法URL请求头请求数据预期结果实际结果

分析测试点

与功能测试时分析差不多。

Postman的使用

基本使用

1、请求方法
2、URL
3、请求头
4、请求体
5、断言
在这里插入图片描述

断言

在这里插入图片描述
pm:postman的实例
test():postman的测试方法

  • 断言响应状态码
// 断言响应状态码200
pm.test("Status code is 200", function () {pm.response.to.have.status(200);
});

参数一:“Status code is 200” ==》 给用户看的提示信息
参数二:“function () { pm.response.to.have.status(200);}” ==》预期结果是200。

  • 断言包含的某个字符串
pm.test("Body matches string", function () {pm.expect(pm.response.text()).to.include("string_you_want_to_search");
});

参数一:“Body matches string” ==》 给用户看的提示信息
参数二:“function () {pm.expect(pm.response.text()).to.include(“预期字符串”);}” ==》预期结果是应该包含“预期字符串”·

  • 断言JSON数据
pm.test("Your test name", function () {var jsonData = pm.response.json();pm.expect(jsonData.value).to.eql(100);
});

参数一:“Your test name” ==》 给用户看的提示信息
参数二:“function () {var jsonData = pm.response.json();
pm.expect(jsonData.value).to.eql(100);}” ==》预期结果json中某个“value”的key值为“100”

原理:
在这里插入图片描述

关联

// 1、获取响应数据、转为json格式,保存到变量jsonDta 中。
var jsonDta = pm.response.json()
// 2.1、设置环境变量。
pm.environment.set( "环境变量名",环境变量值)
// 2.2、使用全局变量做容器。
pm.globals.set ( "全局变量名",全局变量值)
// 3.在postman界面中提取全局、环境变量数据。
{{全局变量名}}/{{环境变量名}}
var value = pm.environment.get("var_name");	# 代码中引用

创建环境:
全局变量:在整个postman中都可以使用的变量。不需要单独创建环境。

环境变量:在特定的环境下,才能使用的变量。需要给此变量创建单独的环境。
在这里插入图片描述

例子一(方法步骤):

  • 1、发送获取天气的请求,获取响应结果,存入全局变量
    在这里插入图片描述

  • 2、百度搜索接口从全局变量中,取城市名,完成搜索
    在这里插入图片描述

例子二(令牌的存储):
在这里插入图片描述

在这里插入图片描述

参数化

  • CSV 文件
    注意:
    1、不能测试 bool类型。因为postman读取csv后,将所有非数值类型数据,自动添加“”变为字符串
    2、不能存储复杂数据类型(元组,列表,字典)
    3、不能实现参数测试

  • JSON文件
    文件大
    例如:
    1、创建导入
    在这里插入图片描述

2、引用数据文件
(1、){{keyi}} 引用相关对象的key
(2、)使用postman内置的关键字data
例如: csv文件:data.字段名;
json文件:data.键名;
在这里插入图片描述
只能按第一图进行操作,然后点击run.

测试报告

前提:
1、安装node.js
2、安装newman【npm install -g newman】【newman -v 测试安装成功】
3、安装newman插件【npm install -g newman-reporter-htmlextra】

1、导出用例:
在这里插入图片描述
2、导出环境:
在这里插入图片描述

3、命令生成:
在这里插入图片描述
在这里插入图片描述
newman run 用例集文件.json -e 环境文件 -r htmlextra --reporter-htmlextra-export aa.html

Postman练习

1、项目结构
在这里插入图片描述
2、创建环境
3、编写测试用例
4、执行测试用例(批量执行)

接口自动化

Requests库

Requests库是Python编写的,基于urllib 的HTTP库,使用方便。
安装: pip install requests

请求语法:

在这里插入图片描述
在这里插入图片描述

Cookie

简介: 工程师 针对 http协议是无连接、无状态特性,设计的 一种技术。可以在浏览器端 存储用户的信息。
。 cookie 用于存储 用户临时的不敏感信息。
。 cookie 位于浏览器端。默认大小 4k(可以调整)
。 cookie 中的数据,可以随意被访问,没有安全性可言。
。 cookie 中存储的数据类型,受浏览器限制。

身份认证方式:
。 token
。 cookie +session

cookie +session认证
【就是把获取验证码的请求得到cookie,然后登录时带过去】
在这里插入图片描述

Session

简介: 也叫会话。通常出现在网络通信中,从客户端借助访问终端登录上服务器,直到退出登录所产生的通信数据,保存在会话中。
。 Session用于存储 用户的信息。
。 Session位于服务端。大小自接使用服务器大小。
。 Session 中的数据,不能随意被访问,安全性高。
。 Session中存储的数据类型,受服务器限制。
。 Session自动管理Cookie

session认证
在这里插入图片描述
获取指定响应数据:
在这里插入图片描述

Cookie和Seesion的区别

CookieSeesion
存储位置浏览器服务器
安全性可随意获取加密存储
数据类型受浏览器限制较少直接使用服务器,支持使用所有数据类型
大小默认4K约为服务器大小

参数化

1、读取Jason文件

def read_json_data():list_data = []# 从 .json 文件中,读取 [{},{},{}] 数据with open("./params_data.json", "r", encoding="utf-8") as f:data = json.load(f)for item in data:tmp = tuple(item.values())  # 将字典里的values转成元组list_data.append(tmp)		# 转成[(),(),(),()]格式return list_data

2、参数化
步骤:

  • 导包:from parameterized import parameterized
  • 在通用测试方法上添加一行
  • 并传参元组列表数据(调用自己封装的读取json文件的函数):@parameterized.expand(read_json_data())
  • 修改通用测试方法形参,与json文件中的key保持一致
  • 在通用测试方法内,使用形参
@parameterized.expand(read_json_data())
def test_add(self, cu1, cu2, cu3):res = add(cu1, cu2)self.assertEqual(cu3, res)

接口自动化框架

类似于分层架构,分包管理

在这里插入图片描述

目录结构:

在这里插入图片描述
首先在接口对象成,编写用例代码,然后在接口用例成执行代码,其中断言需要调用工具包的方法,数据需要调用数据文件

接口对象层

文件夹名:api
文件名:hr_login_api

import requests
class HrLoginApi(object):# 登录@classmethoddef login(cls, json_data):url = "http://xxxxxxxx/xxxx/login"header = {"Content-Type": "application/json"}resp = requests.post(url=url, headers=header, json=json_data)return resp

接口用例层

文件夹名:scripts
文件名:test_hr_login

import unittest
from parameterized import parameterized
from api.hr_login_api import HrLoginApi
from common.assert_util import assert_util
from common.read_json_util import read_json_dataclass TestHrLogin(unittest.TestCase):@parameterized.expand(read_json_data())def test_login_sucess(self, desc, req_data, status_code, success, code, message):# 调用自己的接口resp = HrLoginApi.login(req_data)# 断言assert_util(self, resp, status_code, success, code, message)

工具类

文件夹名:common
文件名:assert_util

# 定义通用断言方法
def assert_util(self, resp, status_code, success, code, message):self.assertEqual(status_code, resp.status_code)self.assertEqual(success, resp.json().get("success"))self.assertEqual(code, resp.json().get("code"))self.assertIn(message, resp.json().get("message"))

文件夹名:common
文件名:read_json_util.py

# 定义函数读取Json文件
import jsondef read_json_data():list_data = []with open("../data/params_data.json", "r", encoding="utf-8") as f:json_data = json.load(f)for item in json_data:tem = tuple(item.values())list_data.append(tem)return list_data

数据文件

文件夹名:data
文件名:params_data.json

[{"desc": "登录成功","req_data":{"mobile": "13800000002", "password": "123456"},"status_code": 200,"success": true,"code": 1000,"message": "操作成功"},{"desc": "手机号为空","req_data":{"mobile": null, "password": "123456"},"status_code": 200,"success": false,"code": 2001,"message": "用户名或密码错误"}
]

测试报告文件

日志收集

log:记录当前系统运行是的信息。
作用:查看系统是否正常,定位bug

日志级别:日志级别与严重性相反 ,只会记录比自己低级别和相同级别的日志
在这里插入图片描述
代码如下:

"""
步骤:
# 0. 导包
# 1. 创建日志器对象
# 2. 设置日志打印级别# logging.DEBUG 调试级别# logging.INFO 信息级别# logging.WARNING 警告级别# logging.ERROR 错误级别# logging.CRITICAL 严重错误级别
# 3. 创建处理器对象# 创建 输出到控制台 处理器对象# 创建 输出到日志文件 处理器对象
# 4. 创建日志信息格式
# 5. 将日志信息格式设置给处理器# 设置给 控制台处理器# 设置给 日志文件处理器
# 6. 给日志器添加处理器# 给日志对象 添加 控制台处理器# 给日志对象 添加 日志文件处理器
# 7. 打印日志
"""
import logging.handlers
import logging
import time# 1. 创建日志器对象
logger = logging.getLogger()# 2. 设置日志打印级别
logger.setLevel(logging.DEBUG)
# logging.DEBUG 调试级别
# logging.INFO 信息级别
# logging.WARNING 警告级别
# logging.ERROR 错误级别
# logging.CRITICAL 严重错误级别# 3.1 创建 输出到控制台 处理器对象
st = logging.StreamHandler()
# 3.2 创建 输出到日志文件 处理器对象
fh = logging.handlers.TimedRotatingFileHandler('a.log', when='midnight', interval=1,backupCount=3, encoding='utf-8')
# when 字符串,指定日志切分间隔时间的单位。midnight:凌晨:12点。
# interval 是间隔时间单位的个数,指等待多少个 when 后继续进行日志记录
# backupCount 是保留日志文件的个数# 4. 创建日志信息格式
fmt = "%(asctime)s %(levelname)s [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s"
formatter = logging.Formatter(fmt)# 5.1 日志信息格式 设置给 控制台处理器
st.setFormatter(formatter)
# 5.2 日志信息格式 设置给 日志文件处理器
fh.setFormatter(formatter)# 6.1 给日志器对象 添加 控制台处理器
logger.addHandler(st)
# 6.2 给日志器对象 添加 日志文件处理器
logger.addHandler(fh)# 7. 打印日志
while True:# logging.debug('我是一个调试级别的日志')logging.info('我是一个信息级别的日志')# logging.warning('我是一个警告级别的日志')# logging.error('我是一个错误级别的日志')# logging.critical('我是一个严重错误级别的日志')time.sleep(1)

封装成工具类:

import logging.handlers
import logging
import timedef init_log_config(filename, when='midnight', interval=1, backup_count=7):"""功能:初始化日志配置函数:param filename: 日志文件名:param when: 设定日志切分的间隔时间单位 ’mindnight’:param interval: 间隔时间单位的个数,指等待多少个 when 后继续进行日志记录:param backup_count: 保留日志文件的个数:return:"""# 1. 创建日志器对象logger = logging.getLogger()# 2. 设置日志打印级别logger.setLevel(logging.DEBUG)# logging.DEBUG 调试级别# logging.INFO 信息级别# logging.WARNING 警告级别# logging.ERROR 错误级别# logging.CRITICAL 严重错误级别# 3. 创建处理器对象# 控制台对象st = logging.StreamHandler()# 日志文件对象fh = logging.handlers.TimedRotatingFileHandler(filename,when=when,interval=interval,backupCount=backup_count,encoding='utf-8')# 4. 日志信息格式fmt = "%(asctime)s %(levelname)s [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s"formatter = logging.Formatter(fmt)# 5. 给处理器设置日志信息格式st.setFormatter(formatter)fh.setFormatter(formatter)# 6. 给日志器添加处理器logger.addHandler(st)logger.addHandler(fh)if __name__ == '__main__':# 初始化日志init_log_config('a.log')# 打印输出日志信息logging.debug('我是一个调试级别的日志')

全量字段校验

1、定义校验规则
2、比对

unittest框架

主要用于管理测试用例。

注意遵守标识符命名规范:

  • 只能使用 字母、数字、下划线。数字不能开头。避免使用 关键字、已知函数名
  • 类:(首字母必须大写。建议以 Test 开头)
  • 方法:(必须 test 开头,建议 编号)

基本用法测试

  • 1、创建测试用例
import requests
# 导包
import unittestclass Test01(unittest.TestCase):    # 使用TestCase,创建测试用例# def 定义的test_ 是测试用例,只有执行 if __name__ == '___mian___'# 的时候会执行测试用例,其他普通函数则不执行,通过 self 来调用执行。def test_01(self):  # 测试方法必须以test_开头url = 'https://apiuat.jaspervault.io/api/options-dates/'params = {'network_id': 1, 'collateral_token_id': 1, 'collateral_token_id': 2, 'collateral_token_id': 'hedge'}req = requests.get(url, params)print(req.json())
  • 2、执行测试用例
import unittest
from htmltestreport import HTMLTestReport
from test.test_aa03 import Test01# TestSuite,测试套件
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(Test01))  # 将测试类的所有方法进行添加runner = unittest.TextTestRunner()  # 实例化运行对象runner = HTMLTestReport("测试报告1.html")   # 自动化生成测试报告runner.run(suite)   # 运行对象
  • 批量增加测试方法到测试套件中
    写法:
  1. suite = unittest.TestLoader().discover(“指定搜索的目录文件”,“指定字母开头模块文件”)
  2. suite = unittest.defaultTestLoader.discover(“指定搜索的目录文件”,“指定字母开头模块文件”) 【推荐】
    注意:
    如果使用写法1,TestLoader()必须有括号。

Fixture(测试夹具)

是一种代码结构,在某些特定情况下,会自动执行。
方法级别:每个方法执行前后都要执行
def setUp(),每个测试方法执行之前都会执行 (初始化)
def tearDown(),每个测试方法执行之后都会执行 (释放)

类级别:在每个测试类中所有方法执行前后 都会自动调用的结构(在整个类中 执行之前执行之后各一次)
类方法必须使用 @classmethod修饰
def setUpClass() ,类中所有方法之前
def tearDownClass(),类中所有方法之后

断言

  • 断言的结果:
    1、True,用例通过
    2、False,代码抛出异常,用例不通过
    3、在unittest中使用断言,需要通过 self.断言方法

  • 常用的断言:

self.assertEqual(ex1, ex2) 			# 判断ex1 是否和ex2 相等
self.assertIn(ex1, ex2) 			#  ex2是否包含 ex1   注意:所谓的包含不能跳字符
self.assertTrue(ex)					#  判断ex是否为True重点讲前两个assertEqual 和 assertIn
方法:
assertEqual:self.assertEqual(预期结果,实际结果)          判断的是预期是否相等实际
assertIn:self.assertIn(预期结果,实际结果) 				判断的是预期是否包含实际中
assertIn('admin', 'admin') 								# 包含
assertIn('admin', 'adminnnnnnnn') 						# 包含
assertIn('admin', 'aaaaaadmin') 						# 包含
assertIn('admin', 'aaaaaadminnnnnnn') 					# 包含
assertIn('admin', 'addddddmin') 						# 不是包含

标准语法

import unittestclass demo(unittest.TestCase):@classmethoddef setUpClass(cls) -> None:    # 执行该类前进行print("setUpClass")@classmethoddef tearDownClass(cls) -> None:print("tearDownClass")      # 执行该类后进行def setUp(self) -> None:    # 执行每个用例前进行print("setup")def tearDown(self) -> None:     # 执行每个用例后进行print("down")def test_case01(self):print("test_case01")self.assertEqual(1, 1, "判断相等")@unittest.skip  # 跳过该用例def test_case02(self):print("test_case02")self.assertEqual(2, 1, "判断相等")

PyMySQL操作数据库

使用场景:校验测试数据(字段会改,返回数据没有),构造测试数据(数据只能用一次,无法保证数据已存在)

安装

pip install pymysql

操作步骤

在这里插入图片描述
1.导包import pymysql
2.创建连接。conn=pymysql.connect()
3.获取游标。cursor=conn.cursor()
4.执行 SQL。cursor.execute("sql语句”)
5.关闭游标,关闭连接

1.导包

import pymysql

2、创建连接

conn = pymysql.connect(host=" ",port=0,user=" ",password=" ",database=" ",charset=" ")
host:数据库所在IP地址
port:端口
user:用户名
password:密码
database:连接那个数据库的名字
charset:字符集。utf8

conn :数据库对象

3、获取游标

cursor=conn.cursor()

4、执行SQL语句

。 查询语句
提取数据:cursor.fetch*()

。 增删改语句
成功:提交事务 conn.commit()
失败:回滚事务 conn.rollback()
【查询】
cursor.fetch*() #默认在0号位,提取数据在所在位置的下一行,每提一条自动向下移动。
。。 fetchone():从结果集中,提取一行。
。。 fetchmany(size):从结果集中,提取 size 行。
。。 fetchall():提取所有结果集。
。。 属性rownumber:可以设置游标位置。 cursor.rownumber=0
【添加】【修改】
。。 成功:提交事务 conn.commit()
。。 失败:回滚事务 conn.rollback()

5、关闭

关闭游标:cursor.close()
关闭连接:conn.close()

封装

pymysql封装

import pymysqlclass DBUtil(object):conn = Nonecursor = None@classmethoddef __get_conn(cls):if cls.conn is None:cls.conn = pymysql.connect(host="127.0.0.1", port=3306, user="root",password="root", database="test01", charset="utf8")return cls.conn@classmethoddef __close_conn(cls):if cls.conn is not None:cls.conn.close()cls.conn = Nonepass@classmethoddef select_one(cls, sql):try:# 获取连接cls.conn = cls.__get_conn()# 执行sql语句cursor = cls.conn.cursor()cursor.execute(sql)res = cursor.fetchone()except Exception as err:print("查询sql错误", str(err))finally:# 关闭连接cls.__close_conn()return res@classmethoddef uid_db(cls, sql):cursor = Nonetry:# 连接cls.conn = cls.__get_conn()cursor = cls.conn.cursor()# sqlcursor.execute(sql)print("影响行数:", cls.conn.affected_rows())# 提交cls.conn.commit()except Exception as err:print("err", err)# 回滚cls.conn.rollback()finally:# 关闭cursor.close()cls.__close_conn()

测试

简单来说就是将重复的代码进行封装

  • 接口对象层
    封装接口的
  • 测试脚本层
    封装断言的

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词