文章目录
-
- 一、项目背景
-
- [1.1 为什么选择接口自动化测试](#1.1 为什么选择接口自动化测试)
- [1.2 系统业务模块介绍](#1.2 系统业务模块介绍)
- 二、技术选型
-
- [2.1 为什么选择Pytest](#2.1 为什么选择Pytest)
- [2.2 为什么选择YAML作为数据存储格式](#2.2 为什么选择YAML作为数据存储格式)
- 三、核心模块
-
- [3.1 HTTP请求封装](#3.1 HTTP请求封装)
- [3.2 YAML数据管理](#3.2 YAML数据管理)
- [3.3 JSON Schema响应校验](#3.3 JSON Schema响应校验)
- [3.4 测试数据生成工具](#3.4 测试数据生成工具)
- 四、挑选接口
- 五、项目结构
-
- [5.1 Pytest配置文件](#5.1 Pytest配置文件)
- 六、测试用例设计(带脑图)
-
- [6.1 测试用例设计原则](#6.1 测试用例设计原则)
- [6.2 测试用例示例(脑图)](#6.2 测试用例示例(脑图))
- 七、编写代码
-
- [7.1 用户注册接口](#7.1 用户注册接口)
- [7.2 登录接口](#7.2 登录接口)
- [7.3 创建活动接口](#7.3 创建活动接口)
- [7.4 抽奖接口](#7.4 抽奖接口)
- 八、数据流转
-
- [8.1 数据流转设计思想](#8.1 数据流转设计思想)
- [8.2 完整数据流转链路](#8.2 完整数据流转链路)
- [8.3 测试用例执行顺序](#8.3 测试用例执行顺序)
- 九、测试报告
-
- [9.1 测试用例统计](#9.1 测试用例统计)
- [9.2 异常场景覆盖](#9.2 异常场景覆盖)
- [9.3 allure 生成测试报告](#9.3 allure 生成测试报告)
- 十、优化方向
-
- [10.1 短期优化](#10.1 短期优化)
- [10.2 长期扩展](#10.2 长期扩展)
- 十一、总结

一、项目背景
在企业级应用开发中,抽奖系统是一个常见的业务场景,广泛应用于电商促销、年会活动、用户运营等多种场景。一个完整的抽奖系统涉及用户管理、奖品管理、活动管理、抽奖逻辑等多个模块,系统复杂度较高,接口数量众多。为了保证系统的稳定性和可靠性,我们需要对各个API接口进行全面的自动化测试。
1.1 为什么选择接口自动化测试
相比于UI自动化测试,接口自动化测试具有显著优势:
执行效率更高:接口测试直接与后端服务交互,无需加载前端页面,单次测试执行时间通常在毫秒级别。一个包含上百个测试用例的接口测试套件,通常可以在几分钟内完成执行,而同样的UI测试可能需要数小时。
稳定性更强:接口测试不依赖前端页面的元素定位,不会因为页面结构变化、样式调整、动态加载等因素导致测试失败。只要接口契约不变,测试用例就能稳定运行。
覆盖更全面:接口测试可以覆盖更多业务场景和边界条件,包括正常流程、异常流程、边界值、并发场景等。同时,接口测试更容易实现参数化和数据驱动,可以用较少的代码覆盖更多的测试场景。
投入产出比更高:接口自动化测试框架搭建相对简单,学习曲线平缓,团队成员可以快速上手。维护成本也相对较低,适合长期投入。
1.2 系统业务模块介绍
本抽奖系统主要包含以下核心业务模块:
用户管理模块:支持管理员和普通用户两种角色的注册、登录功能。管理员拥有系统管理权限,可以创建活动和管理奖品;普通用户作为活动参与者,可以参与抽奖活动。系统通过Token机制实现用户认证和权限控制。
奖品管理模块:管理员可以创建、编辑、删除奖品信息。每个奖品包含名称、描述、价格、图片等属性。奖品创建时需要上传奖品图片,支持多种图片格式。奖品可以关联到多个活动中,实现奖品的复用。
活动管理模块:管理员可以创建抽奖活动,关联奖品和参与用户。创建活动时需要设置活动名称、描述、奖品列表(包含奖品等级和中奖人数)、参与用户列表。系统支持多种奖品等级,如一等奖、二等奖、三等奖等。
抽奖执行模块:根据活动配置执行抽奖操作,记录中奖信息。抽奖时需要指定活动ID、奖品ID、奖品等级、中奖时间、中奖用户列表等信息。系统会校验活动的有效性、奖品的可用性、用户的参与资格等。
中奖记录模块:展示中奖信息,支持按活动查询中奖记录,提供活动详情查看功能。用户可以查看自己参与活动中的中奖情况,管理员可以查看所有活动的抽奖结果。
二、技术选型
选择以下技术进行代码编写:
| 技术 | 版本 | 说明 |
|---|---|---|
| Python | 3.12 | 编程语言,简洁优雅的语法、丰富的第三方库生态、强大的数据处理能力 |
| Pytest | 8.3.4 | 测试框架,提供简洁的用例编写方式、强大的参数化功能、丰富的插件生态、详细的失败信息输出 |
| Requests | 2.32.3 | HTTP客户端库,API设计简洁直观,支持Session管理、文件上传、超时控制等特性 |
| JSON Schema | 4.23.0 | 响应数据校验,精确描述响应数据的类型、必填字段、取值范围等约束条件,支持复杂的嵌套结构 |
| PyYAML | 6.0.2 | 数据文件管理,YAML格式简洁易读,支持复杂的数据结构,适合作为测试数据配置文件 |
| Pytest-Ordering | 0.6 | 执行顺序控制,在存在数据依赖的场景下,合理的执行顺序是测试成功的关键 |
2.1 为什么选择Pytest
Pytest是Python生态中最流行的测试框架之一,相比unittest等传统框架,Pytest具有以下优势:
简洁的用例编写 :不需要继承TestCase类,不需要特定的命名规则,只需要以test_开头的函数即可作为测试用例。测试代码更加简洁,可读性更强。
强大的参数化功能 :通过@pytest.mark.parametrize装饰器,可以轻松实现数据驱动测试,用一套代码覆盖多组测试数据。
丰富的插件生态:Pytest拥有丰富的插件生态,如pytest-html(生成HTML报告)、pytest-ordering(控制执行顺序)、pytest-rerunfailures(失败重试)、allure-pytest(生成Allure报告)等。
详细的失败信息:Pytest在测试失败时会输出详细的错误信息,包括断言表达式、实际值、期望值等,便于快速定位问题。
灵活的fixture机制:通过fixture可以实现测试前置条件、测试数据准备、测试后清理等功能,支持fixture之间的依赖和复用。
2.2 为什么选择YAML作为数据存储格式
YAML(YAML Ain't Markup Language)是一种人类可读的数据序列化格式,相比JSON和XML,YAML具有以下优势:
简洁易读:YAML使用缩进表示层级关系,语法简洁,可读性强。对于配置文件和测试数据来说,可读性是非常重要的。
支持注释:YAML支持添加注释,可以在数据文件中添加说明信息,便于理解和维护。
支持复杂数据结构:YAML支持列表、字典、嵌套结构等复杂数据类型,可以表示任意复杂的数据结构。
支持多文档 :一个YAML文件可以包含多个文档,用---分隔,适合存储多个独立的配置项。
三、核心模块
3.1 HTTP请求封装
将Requests库进行二次封装,统一管理请求配置和异常处理。使用Session对象自动管理Cookie和连接池,提高请求效率。
封装的主要功能包括:
Session管理:使用Session对象自动管理Cookie和连接池,避免每次请求都建立新的连接,提高请求效率。同时,Session可以自动处理Cookie,对于需要登录态的接口非常方便。
超时控制:统一设置请求超时时间,避免因网络问题导致测试长时间阻塞。默认超时时间设置为10秒,可以根据实际情况调整。
异常处理 :提供raise_for_status参数控制是否自动抛出HTTP错误。对于正常测试场景,开启此选项可以自动检测HTTP状态码;对于需要测试异常响应的场景,可以关闭此选项手动处理。
统一入口:所有请求都通过封装后的Request类发起,便于统一添加日志、鉴权、重试等通用逻辑。
python
import requests
host = "http://****"
class Request:
def __init__(self):
self.session = requests.Session()
self.timeout = 10
def get(self, url, params=None, headers=None, raise_for_status=True, **kwargs):
full_url = host + url if not url.startswith('http') else url
response = self.session.get(full_url, params=params, headers=headers,
timeout=self.timeout, **kwargs)
if raise_for_status:
response.raise_for_status()
return response
def post(self, url, json=None, data=None, headers=None, files=None,
raise_for_status=True, **kwargs):
full_url = host + url if not url.startswith('http') else url
response = self.session.post(full_url, json=json, data=data, headers=headers,
files=files, timeout=self.timeout, **kwargs)
if raise_for_status:
response.raise_for_status()
return response
3.2 YAML数据管理
使用YAML文件存储和管理测试数据,实现数据的持久化和复用。支持通过点分隔的键路径访问嵌套数据,如register.admin.last.phoneNumber。
数据管理的设计思路:
数据持久化:将测试过程中产生的数据(如用户ID、Token、活动ID等)存储到YAML文件中,实现数据的持久化。这样即使测试中断,也可以从上次的状态继续执行。
数据复用:多个测试用例可以共享同一份数据,避免重复创建测试数据。例如,用户注册后保存的用户信息,可以在登录、创建活动等多个测试用例中使用。
数据隔离:通过键路径的方式组织数据,不同模块的数据存储在不同的命名空间下,避免数据冲突。
默认值支持:当数据不存在时,可以返回默认值,避免因数据缺失导致测试失败。
python
import yaml
import os
def get_yaml(yaml_path, key, default=None):
"""获取YAML文件中的数据,支持点分隔的键路径"""
keys = key.split('.')
if not os.path.exists(yaml_path):
return default
with open(yaml_path, 'r', encoding='utf-8') as f:
data = yaml.safe_load(f) or {}
for k in keys:
if data is None or not isinstance(data, dict):
return default
data = data.get(k)
return data if data is not None else default
def set_yaml(yaml_path, key, value):
"""设置YAML文件中的数据,支持点分隔的键路径"""
keys = key.split('.')
if os.path.exists(yaml_path):
with open(yaml_path, 'r', encoding='utf-8') as f:
data = yaml.safe_load(f) or {}
else:
data = {}
current = data
for k in keys[:-1]:
if k not in current:
current[k] = {}
current = current[k]
current[keys[-1]] = value
with open(yaml_path, 'w', encoding='utf-8') as f:
yaml.dump(data, f, allow_unicode=True, default_flow_style=False)
3.3 JSON Schema响应校验
使用JSON Schema对API响应进行严格校验,确保响应数据结构正确。通过声明式的方式描述预期结构,任何不符合预期的响应都会被检测出来。
JSON Schema校验的优势:
结构校验:可以校验响应数据的整体结构,包括字段是否存在、字段类型是否正确、必填字段是否完整等。
类型约束:可以精确指定每个字段的类型,如字符串、数字、布尔值、数组、对象等。还可以进一步约束字符串的格式、数字的范围等。
灵活配置 :通过additionalProperties可以控制是否允许额外字段,通过required可以指定必填字段,通过enum可以限制枚举值。
错误定位:当校验失败时,JSON Schema会输出详细的错误信息,包括错误路径、错误类型、期望值和实际值等,便于快速定位问题。
python
schema = {
"type": "object",
"required": ["code", "data", "msg"],
"additionalProperties": False,
"properties": {
"code": {"type": "number"},
"data": {"type": ["object", "null"]},
"msg": {"type": "string"}
}
}
from jsonschema import validate
validate(response.json(), schema=schema)
3.4 测试数据生成工具
为了保证测试数据的唯一性和有效性,我们封装了一系列测试数据生成工具:
手机号生成:生成符合中国大陆手机号格式的随机号码,确保每次生成的手机号都是唯一的,避免因手机号重复导致注册失败。
邮箱生成:生成符合标准格式的随机邮箱地址,邮箱域名可以配置,支持多种邮箱格式。
昵称生成:基于手机号或其他唯一标识生成用户昵称,确保昵称的唯一性。
python
import random
import string
def generate_unique_phone():
"""生成唯一的手机号"""
prefixes = ['138', '139', '150', '151', '152', '158', '159', '186', '187', '188']
prefix = random.choice(prefixes)
suffix = ''.join(random.choices(string.digits, k=8))
return prefix + suffix
def generate_unique_email(phone):
"""生成唯一的邮箱"""
return f"test_{phone}@example.com"
def generate_unique_nickname(phone):
"""生成唯一的昵称"""
return f"测试用户_{phone[-4:]}"
四、挑选接口
根据业务需求,挑选以下核心接口进行自动化测试。选择接口的原则是:优先覆盖核心业务流程、高频使用的接口、容易出错的接口。
| 接口名称 | 请求方法 | 接口路径 | 说明 |
|---|---|---|---|
| 用户注册 | POST | /register | 支持管理员和普通用户注册,需要提供手机号、邮箱、密码、昵称等信息 |
| 发送验证码 | GET | /verification-code/send | 发送手机验证码,用于手机号验证场景 |
| 登录 | POST | /password/login | 账号密码登录,返回Token用于后续接口认证 |
| 用户列表 | GET | /base-user/find-list | 查询用户列表,支持分页,可按角色筛选 |
| 创建奖品 | POST | /prize/create | 创建奖品(含图片上传),需要管理员权限 |
| 奖品列表 | GET | /prize/find-list | 查询奖品列表,支持分页 |
| 创建活动 | POST | /activity/create | 创建抽奖活动,需要关联奖品和用户 |
| 活动列表 | GET | /activity/find-list | 查询活动列表,支持分页 |
| 抽奖 | POST | /draw-prize | 执行抽奖操作,记录中奖信息 |
| 中奖记录 | POST | /winning-records/show | 查询中奖记录,按活动ID筛选 |
| 活动详情 | GET | /activity-detail/find | 查询活动详情,包含奖品和用户信息 |
五、项目结构
清晰的项目结构是框架可维护性的基础。采用分层设计的思想,将框架划分为测试用例层、业务逻辑层、接口封装层、工具支持层和数据层。
lottery_system_ApiAutoTest/
├── cases/ # 测试用例目录
│ ├── test_register.py # 用户注册接口测试(30个用例)
│ ├── test_sendcode.py # 验证码发送接口测试(5个用例)
│ ├── test_admin_login.py # 管理员登录接口测试(14个用例)
│ ├── test_user_list.py # 用户列表查询接口测试(3个用例)
│ ├── test_create_prize.py # 奖品创建接口测试(4个用例)
│ ├── test_prize_list.py # 奖品列表查询接口测试(6个用例)
│ ├── test_create_activity.py # 活动创建接口测试(17个用例)
│ ├── test_activity_list.py # 活动列表查询接口测试(6个用例)
│ ├── test_draw_prize.py # 抽奖执行接口测试(8个用例)
│ ├── test_winning_records.py # 中奖记录查询接口测试(3个用例)
│ └── test_activity_detail.py # 活动详情查询接口测试(2个用例)
├── data/ # 数据目录
│ ├── data.yaml # 测试数据存储文件
│ └── images/ # 图片资源目录(用于奖品图片上传)
├── utils/ # 工具类目录
│ ├── request_util.py # HTTP请求封装
│ ├── yaml_util.py # YAML文件操作封装
│ ├── phone_util.py # 手机号生成工具
│ ├── mail_util.py # 邮箱生成工具
│ └── name_util.py # 昵称生成工具
└── pytest.ini # Pytest配置文件
5.1 Pytest配置文件
pytest.ini文件用于配置Pytest的运行行为:
ini
[pytest]
markers =
order: mark test to run in a specific order
testpaths = cases
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts = -v --tb=short
六、测试用例设计(带脑图)
针对每个接口,从以下维度设计测试用例,确保测试覆盖的全面性和有效性:
| 分类 | 说明 | 示例 |
|---|---|---|
| 正向测试 | 正常业务流程,验证接口在正常输入下的行为 | 正常登录、正常创建活动、正常抽奖 |
| 未登录测试 | 无Token访问需要认证的接口,验证权限控制 | 期望返回401或相应错误信息 |
| 参数缺失测试 | 必填参数为空,验证参数校验逻辑 | 用户名为空、密码为空、活动ID为空 |
| 参数格式测试 | 参数格式错误,验证格式校验逻辑 | 手机号格式错误、邮箱格式错误、日期格式错误 |
| 参数长度测试 | 参数长度不符,验证长度校验逻辑 | 密码长度不足或超长、昵称超长 |
| 业务逻辑测试 | 业务规则校验,验证业务逻辑正确性 | 手机号已存在、活动不存在、奖品已抽完 |
| 安全性测试 | 安全防护校验,验证系统安全性 | SQL注入、越权访问、敏感信息泄露 |
6.1 测试用例设计原则
单一职责:每个测试用例只验证一个测试点,避免一个用例包含多个断言。这样当测试失败时,可以快速定位问题所在。
独立性:测试用例之间应该相互独立,不依赖其他测试用例的执行结果。通过数据文件中转的方式实现数据共享,而不是直接依赖。
可重复性:测试用例应该可以重复执行,每次执行的结果应该一致。通过生成唯一的测试数据(如随机手机号)来避免数据冲突。
可维护性:测试用例的代码应该清晰易懂,避免复杂的逻辑。使用参数化来减少重复代码,使用有意义的变量名和函数名。
6.2 测试用例示例(脑图)

七、编写代码
7.1 用户注册接口
用户注册是系统的入口接口,需要验证各种参数组合和业务规则。注册成功后,将用户信息保存到数据文件中,供后续测试使用。
python
import pytest
from utils.request_util import Request
from utils.yaml_util import get_yaml, set_yaml
from utils.phone_util import generate_unique_phone
from utils.mail_util import generate_unique_email
from utils.name_util import generate_unique_nickname
class TestRegister:
url = "/register"
schema = {
"type": "object",
"required": ["code", "data", "msg"],
"properties": {
"code": {"type": "number"},
"data": {"type": ["object", "null"]},
"msg": {"type": "string"}
}
}
@pytest.mark.order(3)
@pytest.mark.parametrize("identity,password", [("ADMIN", "Whx041223")])
def test_register_admin_success(self, identity, password):
phone = generate_unique_phone()
name = generate_unique_nickname(phone)
mail = generate_unique_email(phone)
data = {
"name": name,
"mail": mail,
"phoneNumber": phone,
"password": password,
"identity": identity
}
r = Request().post(url=self.url, json=data)
validate(r.json(), schema=self.schema)
assert r.json()["code"] == 200
assert r.json()["msg"] == "成功"
register_data = {
"phoneNumber": phone,
"password": password,
"identity": identity,
"name": name,
"mail": mail
}
set_yaml("data/data.yaml", "register.admin.last", register_data)
7.2 登录接口
登录接口用于获取用户Token,Token是后续接口认证的关键。登录成功后,将Token保存到数据文件中。
python
import pytest
from utils.request_util import Request
from utils.yaml_util import get_yaml, set_yaml
from jsonschema import validate
class TestAdminLogin:
url = "/password/login"
schema = {
"type": "object",
"required": ["code", "data", "msg"],
"properties": {
"code": {"type": "number"},
"data": {
"type": "object",
"properties": {
"token": {"type": "string"},
"identity": {"type": "string"}
}
},
"msg": {"type": "string"}
}
}
@pytest.fixture
def login(self):
return {
"loginName": get_yaml("data/data.yaml", "register.admin.last.phoneNumber"),
"password": get_yaml("data/data.yaml", "register.admin.last.password"),
"mandatoryIdentity": "ADMIN"
}
@pytest.mark.order(8)
def test_admin_login_success(self, login):
data = {
"loginName": login["loginName"],
"password": login["password"],
"mandatoryIdentity": login["mandatoryIdentity"]
}
r = Request().post(url=self.url, json=data)
validate(r.json(), schema=self.schema)
assert r.json()["code"] == 200
token = {
"user_token": r.json()["data"]["token"],
"identity": r.json()["data"]["identity"]
}
set_yaml("data/data.yaml", "data", token)
7.3 创建活动接口
创建活动是一个复杂的接口,需要关联奖品和用户数据。活动创建成功后,将活动ID及关联数据保存到数据文件中,供抽奖接口使用。
python
import pytest
from datetime import datetime
from utils.request_util import Request
from utils.yaml_util import get_yaml, set_yaml
from jsonschema import validate
class TestCreateActivity:
url = "/activity/create"
schema = {
"type": "object",
"required": ["code", "data", "msg"],
"properties": {
"code": {"type": "number"},
"data": {
"type": "object",
"properties": {
"activityId": {"type": "number"}
}
},
"msg": {"type": "string"}
}
}
@pytest.mark.order(19)
def test_create_activity_success(self):
prize_ids = get_yaml("data/data.yaml", "prize.list.prizeId", default=[])
users = get_yaml("data/data.yaml", "user.list.normal_users", default=[])
activity_name = f"抽奖测试_{datetime.now().strftime('%Y%m%d%H%M%S')}"
body = {
"activityName": activity_name,
"description": "年会抽奖活动",
"activityPrizeList": [
{"prizeId": prize_ids[0], "prizeAmount": 1, "prizeTiers": "FIRST_PRIZE"}
],
"activityUserList": users[:3]
}
header = {"user_token": get_yaml("data/data.yaml", "data.user_token")}
r = Request().post(url=self.url, json=body, headers=header)
validate(r.json(), schema=self.schema)
assert r.json()["code"] == 200
set_yaml("data/data.yaml", "activity.create", {
"activityId": r.json()["data"]["activityId"],
"activityPrizeList": body["activityPrizeList"],
"activityUserList": body["activityUserList"]
})
7.4 抽奖接口
抽奖接口是系统的核心业务接口,需要遍历活动中的所有奖品进行抽奖。每个奖品抽奖成功后,系统会记录中奖信息。
python
import pytest
from datetime import datetime
from utils.request_util import Request
from utils.yaml_util import get_yaml
from jsonschema import validate
class TestDrawPrize:
url = "/draw-prize"
schema = {
"type": "object",
"required": ["code", "data", "msg"],
"properties": {
"code": {"type": "number"},
"data": {"type": ["object", "null"]},
"msg": {"type": "string"}
}
}
@pytest.mark.order(24)
def test_draw_prize_success(self):
activity = get_yaml("data/data.yaml", "activity.create", default={})
prize_list = activity.get("activityPrizeList", [])
user_list = activity.get("activityUserList", [])
header = {"user_token": get_yaml("data/data.yaml", "data.user_token")}
for prize in prize_list:
body = {
"activityId": activity.get("activityId"),
"prizeId": prize.get("prizeId"),
"prizeTiers": prize.get("prizeTiers"),
"winningTime": datetime.now().strftime("%Y-%m-%dT%H:%M:%S.000Z"),
"winnerList": user_list[:prize.get("prizeAmount", 1)]
}
r = Request().post(url=self.url, json=body, headers=header)
validate(r.json(), schema=self.schema)
assert r.json()["code"] == 200
八、数据流转
8.1 数据流转设计思想
在接口自动化测试中,测试数据的管理是一个核心问题。很多接口之间存在数据依赖关系,例如:创建活动需要先获取奖品ID和用户ID,抽奖需要先获取活动ID。如何管理这些数据依赖,是框架设计的关键。
采用"数据文件中转"的设计思想,将测试过程中产生的数据持久化存储到YAML文件中,后续测试从文件中读取所需数据。
这种方式的优点:
解耦测试用例:测试用例之间通过数据文件间接关联,降低了耦合度。每个测试用例只需要关心自己需要什么数据、产出什么数据,不需要关心数据的来源和去向。
支持增量测试:可以单独执行某个测试用例,只要数据文件中存在所需数据即可。例如,如果已经注册过用户,可以直接执行登录测试,不需要重新注册。
便于问题排查:数据文件记录了测试过程中的所有数据,当测试失败时,可以查看数据文件中的数据是否正确,便于定位问题。
支持测试回放:可以使用固定的数据文件进行测试回放,复现问题场景。
8.2 完整数据流转链路
以下是测试用例执行过程中的完整数据流转链路:
注册成功 → 保存用户数据(手机号、密码、邮箱、用户ID)
↓
登录成功 → 保存Token和身份信息
↓
用户列表 → 保存NORMAL用户ID和姓名
↓
奖品列表 → 保存奖品ID列表
↓
创建活动 → 使用奖品ID和用户数据,保存活动ID及关联数据
↓
抽奖 → 使用活动ID、奖品ID、用户数据执行抽奖
↓
中奖记录/活动详情 → 使用活动ID查询结果
8.3 测试用例执行顺序
使用pytest-ordering插件控制测试用例执行顺序,确保数据依赖正确。执行顺序的设计原则是:先执行数据生产者,后执行数据消费者。
| Order | 测试模块 | 测试内容 | 数据产出 |
|---|---|---|---|
| 1-4 | 注册模块 | 完成管理员和普通用户注册 | 用户数据(手机号、密码、邮箱等) |
| 5-6 | 验证码模块 | 发送验证码测试 | 无 |
| 7-8 | 登录模块 | 管理员和普通用户登录 | Token、身份信息 |
| 9-10 | 用户列表 | 查询用户列表 | NORMAL用户ID和姓名 |
| 11-16 | 奖品模块 | 创建奖品、获取奖品列表 | 奖品ID列表 |
| 17-21 | 活动模块 | 创建活动、获取活动列表 | 活动ID及关联数据 |
| 22-24 | 抽奖模块 | 执行抽奖操作 | 中奖记录 |
| 25-30 | 记录模块 | 查询中奖记录、活动详情 | 无 |
九、测试报告
9.1 测试用例统计
本项目共设计了98个测试用例,覆盖了11个接口模块。测试用例分布如下:
| 接口模块 | 正向用例 | 异常用例 | 合计 | 覆盖场景 |
|---|---|---|---|---|
| 注册接口 | 2 | 28 | 30 | 参数缺失、格式错误、长度校验、唯一性校验 |
| 验证码接口 | 1 | 4 | 5 | 参数缺失、格式错误 |
| 登录接口 | 1 | 13 | 14 | 参数缺失、格式错误、用户不存在、密码错误 |
| 用户列表接口 | 1 | 2 | 3 | 未登录、参数错误 |
| 创建奖品接口 | 1 | 3 | 4 | 未登录、参数缺失 |
| 奖品列表接口 | 1 | 5 | 6 | 未登录、参数错误 |
| 创建活动接口 | 1 | 16 | 17 | 未登录、参数缺失、格式错误、业务规则 |
| 活动列表接口 | 1 | 5 | 6 | 未登录、参数错误 |
| 抽奖接口 | 1 | 7 | 8 | 未登录、参数缺失、业务规则 |
| 中奖记录接口 | 1 | 2 | 3 | 未登录、参数错误 |
| 活动详情接口 | 1 | 1 | 2 | 未登录、参数错误 |
| 总计 | 12 | 86 | 98 |
9.2 异常场景覆盖
本项目覆盖了以下异常场景,确保系统在各种异常输入下都能正确处理:
- ✅ 参数缺失校验:必填参数为空、请求体为空等场景,验证系统对必填参数的校验逻辑
- ✅ 参数格式校验:手机号格式错误、邮箱格式错误、日期格式错误等,验证系统对参数格式的校验逻辑
- ✅ 参数长度校验:字符串超长、数值超范围等场景,验证系统对参数长度的校验逻辑
- ✅ 业务规则校验:数据不存在、数据已存在、状态不正确等,验证系统对业务规则的校验逻辑
- ✅ 权限校验:未登录访问、越权访问等场景,验证系统的权限控制机制
- ✅ 数据唯一性校验:手机号已存在、邮箱已存在等,验证系统对唯一性约束的处理
- ✅ SQL注入测试:验证系统的安全防护是否到位,防止SQL注入攻击
9.3 allure 生成测试报告

在allure_results中生成测试用例的结果集,然后在allure_report中生成测试报告,测试报告为html页面,页面示例:

查看页面得到测试通过了 100%,涉及92条测试用例,总耗时13秒左右。

并且可以查看每一条测试用例的执行,且有日志打印,可以查看该次测试的具体情况。
十、优化方向
10.1 短期优化
支持多环境配置:将服务器地址、数据库配置等提取到配置文件中,支持开发环境、测试环境、生产环境的快速切换。可以通过命令行参数或环境变量指定当前环境。
yaml
# config.yaml
environments:
dev:
host: "http://dev.example.com"
test:
host: "http://test.example.com"
prod:
host: "http://prod.example.com"
10.2 长期扩展
集成CI/CD流水线:将接口测试集成到持续集成/持续部署流水线中,实现代码提交后自动执行测试。可以与Jenkins、GitLab CI、GitHub Actions等工具集成,在代码合并前自动执行测试,确保代码质量。
添加数据库校验:除了校验接口响应,还可以直接查询数据库验证数据的正确性。例如,创建活动后查询数据库验证活动数据是否正确写入,抽奖后查询数据库验证中奖记录是否正确生成。
支持性能测试:基于现有的接口测试脚本,扩展性能测试功能。可以使用locust等工具进行接口压力测试,验证系统在高并发场景下的性能表现。
十一、总结
本文详细介绍了抽奖系统接口自动化测试框架的设计与实现过程,从项目背景、技术选型、核心模块、测试用例设计、代码实现、数据流转、测试报告等多个方面进行了阐述。
框架设计:采用分层设计思想,将框架划分为测试用例层、业务逻辑层、接口封装层、工具支持层和数据层。各层职责清晰,便于维护和扩展。
核心封装:封装了HTTP请求、YAML数据管理、JSON Schema响应校验等核心功能。通过封装,简化了测试代码的编写,提高了代码的复用性。
测试用例:设计了98个测试用例,覆盖正向测试、异常测试、边界测试等多种场景。测试用例覆盖了参数校验、业务规则、权限控制、安全性等多个维度。
数据流转:通过YAML文件实现测试数据的持久化和跨用例传递,确保测试用例之间的数据依赖正确。数据流转的设计使得测试用例可以独立执行,提高了测试的灵活性。
接口自动化测试是一项需要持续投入和优化的工作。随着业务的发展,接口会不断变化,测试用例也需要相应更新。一个好的测试框架应该具备良好的可维护性和可扩展性,能够快速适应业务的变化。希望本文能够为读者在接口自动化测试实践中提供一些参考和帮助。
项目源码 :lottery_system_ApiAutoTest
技术交流:欢迎在评论区留言讨论,共同进步!