fix bugs
This commit is contained in:
99
test/QUICKSTART.md
Normal file
99
test/QUICKSTART.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# 快速开始 - 5分钟完成API测试
|
||||
|
||||
## 📦 第一步:安装依赖
|
||||
|
||||
```bash
|
||||
cd test
|
||||
npm install
|
||||
```
|
||||
|
||||
## ⚙️ 第二步:配置测试参数
|
||||
|
||||
编辑 `quick-test.js` 或 `api-test.js`,修改以下内容:
|
||||
|
||||
```javascript
|
||||
const config = {
|
||||
baseURL: 'http://your-api-domain.com', // 改成实际的API地址
|
||||
testUser: {
|
||||
username: 'test_user', // 改成测试账号
|
||||
password: 'test_password' // 改成测试密码
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## 🚀 第三步:运行测试
|
||||
|
||||
### 方式1:快速测试(推荐新手)
|
||||
只测试5个核心接口,30秒内完成:
|
||||
```bash
|
||||
npm run test:quick
|
||||
```
|
||||
|
||||
### 方式2:完整测试
|
||||
测试所有14个接口,包含数据清理:
|
||||
```bash
|
||||
npm run test
|
||||
```
|
||||
|
||||
## 📊 查看测试结果
|
||||
|
||||
测试会自动输出结果:
|
||||
```
|
||||
🚀 武术比赛报名系统 - 快速测试
|
||||
|
||||
📍 API地址: http://your-api-domain.com
|
||||
|
||||
🔐 测试登录...
|
||||
✅ 登录成功
|
||||
📋 测试赛事列表...
|
||||
✅ 赛事列表获取成功 (10条数据)
|
||||
👥 测试选手列表...
|
||||
✅ 选手列表获取成功 (25条数据)
|
||||
👤 测试用户信息...
|
||||
✅ 用户信息获取成功 (测试用户)
|
||||
📝 测试报名列表...
|
||||
✅ 报名列表获取成功 (3条数据)
|
||||
|
||||
==================================================
|
||||
📊 测试结果: 5个通过, 0个失败
|
||||
✨ 成功率: 100.0%
|
||||
==================================================
|
||||
```
|
||||
|
||||
## ❌ 常见错误处理
|
||||
|
||||
### 错误1:网络请求失败
|
||||
```
|
||||
❌ 请求失败: connect ECONNREFUSED
|
||||
```
|
||||
**解决方案**:检查 baseURL 是否正确,服务器是否启动。
|
||||
|
||||
### 错误2:登录失败
|
||||
```
|
||||
❌ 登录失败
|
||||
```
|
||||
**解决方案**:检查用户名和密码是否正确。
|
||||
|
||||
### 错误3:Token过期
|
||||
```
|
||||
❌ 业务状态码: 401
|
||||
```
|
||||
**解决方案**:重新运行测试获取新Token。
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
- 查看 `README.md` 了解更多测试方式
|
||||
- 使用 Apifox/Postman 导入 `api-test-collection.json` 进行可视化测试
|
||||
- 配置 CI/CD 自动化测试
|
||||
|
||||
## 💡 提示
|
||||
|
||||
- 建议使用测试环境的API地址,不要直接测试生产环境
|
||||
- 测试会自动清理创建的测试数据
|
||||
- 可以将测试脚本加入 Git 版本控制
|
||||
|
||||
## 📞 需要帮助?
|
||||
|
||||
- 查看详细文档:`test/README.md`
|
||||
- 查看API文档:`API对接方案.md`
|
||||
- 提交Issue获取支持
|
||||
289
test/README.md
Normal file
289
test/README.md
Normal file
@@ -0,0 +1,289 @@
|
||||
# 武术比赛报名系统 - 测试方案指南
|
||||
|
||||
## 📋 目录
|
||||
1. [API接口测试](#api接口测试)
|
||||
2. [自动化测试脚本](#自动化测试脚本)
|
||||
3. [压力测试](#压力测试)
|
||||
4. [数据验证测试](#数据验证测试)
|
||||
|
||||
---
|
||||
|
||||
## 1. API接口测试
|
||||
|
||||
### 方式A:使用Apifox/Postman(推荐)
|
||||
|
||||
**优点**:
|
||||
- 图形化界面,操作简单
|
||||
- 支持环境变量、前置脚本、断言
|
||||
- 可以导入导出测试集合
|
||||
- 支持Mock Server
|
||||
- 团队协作方便
|
||||
|
||||
**步骤**:
|
||||
|
||||
1. **安装工具**
|
||||
- Apifox: https://www.apifox.cn/
|
||||
- Postman: https://www.postman.com/
|
||||
|
||||
2. **导入测试集合**
|
||||
```
|
||||
导入文件: test/api-test-collection.json
|
||||
```
|
||||
|
||||
3. **配置环境变量**
|
||||
```
|
||||
baseUrl: http://your-api-domain.com
|
||||
username: your_username
|
||||
password: your_password
|
||||
```
|
||||
|
||||
4. **运行测试**
|
||||
- 单个接口测试:点击"发送"按钮
|
||||
- 批量测试:使用"测试套件"功能
|
||||
- 定时测试:设置定时任务自动运行
|
||||
|
||||
5. **查看测试报告**
|
||||
- Apifox自动生成测试报告
|
||||
- 包含通过率、响应时间、错误详情
|
||||
|
||||
### 方式B:使用curl命令(简单快速)
|
||||
|
||||
```bash
|
||||
# 1. 登录获取token
|
||||
curl -X POST http://your-api-domain.com/martial/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"test_user","password":"test_password"}'
|
||||
|
||||
# 2. 获取赛事列表(替换YOUR_TOKEN)
|
||||
curl -X GET "http://your-api-domain.com/martial/competition/list?current=1&size=20" \
|
||||
-H "Blade-Auth: Bearer YOUR_TOKEN"
|
||||
|
||||
# 3. 获取选手列表
|
||||
curl -X GET "http://your-api-domain.com/martial/athlete/list?current=1&size=100" \
|
||||
-H "Blade-Auth: Bearer YOUR_TOKEN"
|
||||
|
||||
# 4. 新增选手
|
||||
curl -X POST http://your-api-domain.com/martial/athlete/submit \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Blade-Auth: Bearer YOUR_TOKEN" \
|
||||
-d '{"name":"测试选手","idCard":"110101199001011234","team":"测试队伍"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 自动化测试脚本
|
||||
|
||||
### 使用Node.js测试脚本
|
||||
|
||||
**优点**:
|
||||
- 完全自动化,无需手动操作
|
||||
- 可集成到CI/CD流程
|
||||
- 详细的测试报告
|
||||
- 自动清理测试数据
|
||||
|
||||
**安装依赖**:
|
||||
```bash
|
||||
cd test
|
||||
npm init -y
|
||||
npm install axios
|
||||
```
|
||||
|
||||
**配置测试**:
|
||||
编辑 `test/api-test.js` 文件,修改以下配置:
|
||||
```javascript
|
||||
const config = {
|
||||
baseURL: 'http://your-api-domain.com', // 修改为实际API地址
|
||||
timeout: 10000,
|
||||
testUser: {
|
||||
username: 'test_user', // 修改为测试账号
|
||||
password: 'test_password' // 修改为测试密码
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**运行测试**:
|
||||
```bash
|
||||
node test/api-test.js
|
||||
```
|
||||
|
||||
**测试输出示例**:
|
||||
```
|
||||
============================================================
|
||||
武术比赛报名系统 - API自动化测试
|
||||
============================================================
|
||||
测试开始时间: 2025-12-11 10:30:00
|
||||
API地址: http://your-api-domain.com
|
||||
|
||||
【测试1】用户登录
|
||||
✓ 登录请求HTTP状态码
|
||||
✓ 登录业务状态码
|
||||
✓ 返回Token
|
||||
Token: eyJhbGciOiJIUzI1NiI...
|
||||
|
||||
【测试2】获取轮播图列表
|
||||
✓ 轮播图请求HTTP状态码
|
||||
✓ 轮播图业务状态码
|
||||
✓ 返回数据格式
|
||||
轮播图数量: 3
|
||||
|
||||
...(省略其他测试)
|
||||
|
||||
============================================================
|
||||
测试结果汇总
|
||||
============================================================
|
||||
总测试数: 42
|
||||
通过: 40 ✓
|
||||
失败: 2 ✗
|
||||
成功率: 95.24%
|
||||
耗时: 3.52秒
|
||||
测试结束时间: 2025-12-11 10:30:04
|
||||
============================================================
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 压力测试
|
||||
|
||||
### 使用Apache Bench (ab)
|
||||
|
||||
**测试并发性能**:
|
||||
```bash
|
||||
# 安装ab工具(Windows用户可以安装Apache)
|
||||
# Ubuntu: sudo apt-get install apache2-utils
|
||||
# Mac: brew install ab
|
||||
|
||||
# 测试赛事列表接口(100个请求,10个并发)
|
||||
ab -n 100 -c 10 \
|
||||
-H "Blade-Auth: Bearer YOUR_TOKEN" \
|
||||
http://your-api-domain.com/martial/competition/list?current=1&size=20
|
||||
|
||||
# 测试选手列表接口(1000个请求,50个并发)
|
||||
ab -n 1000 -c 50 \
|
||||
-H "Blade-Auth: Bearer YOUR_TOKEN" \
|
||||
http://your-api-domain.com/martial/athlete/list?current=1&size=100
|
||||
```
|
||||
|
||||
### 使用wrk(更强大的压力测试工具)
|
||||
|
||||
```bash
|
||||
# 安装wrk
|
||||
# Ubuntu: sudo apt-get install wrk
|
||||
# Mac: brew install wrk
|
||||
|
||||
# 基础压力测试(持续30秒,12个线程,400个并发连接)
|
||||
wrk -t12 -c400 -d30s \
|
||||
-H "Blade-Auth: Bearer YOUR_TOKEN" \
|
||||
http://your-api-domain.com/martial/competition/list
|
||||
|
||||
# POST请求压力测试
|
||||
wrk -t12 -c400 -d30s -s post.lua \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Blade-Auth: Bearer YOUR_TOKEN" \
|
||||
http://your-api-domain.com/martial/athlete/submit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 数据验证测试
|
||||
|
||||
### 验证数据完整性
|
||||
|
||||
创建数据验证脚本 `test/data-validation.js`:
|
||||
|
||||
```javascript
|
||||
// 验证所有必填字段
|
||||
async function validateCompetitionData() {
|
||||
const res = await api.get('/martial/competition/list');
|
||||
const competitions = res.data.data.records || res.data.data;
|
||||
|
||||
const issues = [];
|
||||
competitions.forEach((item, index) => {
|
||||
if (!item.id) issues.push(`赛事${index}: 缺少id字段`);
|
||||
if (!item.name && !item.title) issues.push(`赛事${index}: 缺少name/title字段`);
|
||||
if (!item.location && !item.address) issues.push(`赛事${index}: 缺少location/address字段`);
|
||||
});
|
||||
|
||||
return issues;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 测试最佳实践
|
||||
|
||||
### 5.1 测试环境隔离
|
||||
```
|
||||
开发环境: http://dev-api.example.com
|
||||
测试环境: http://test-api.example.com
|
||||
生产环境: http://api.example.com
|
||||
```
|
||||
|
||||
### 5.2 测试数据管理
|
||||
- 使用专门的测试账号
|
||||
- 测试后自动清理数据
|
||||
- 使用可识别的测试数据前缀(如"测试_")
|
||||
|
||||
### 5.3 定期自动化测试
|
||||
使用cron定时执行测试:
|
||||
```bash
|
||||
# 每天凌晨2点执行API测试
|
||||
0 2 * * * cd /path/to/project && node test/api-test.js >> test.log 2>&1
|
||||
```
|
||||
|
||||
### 5.4 CI/CD集成
|
||||
在 `.gitlab-ci.yml` 或 `.github/workflows/test.yml` 中:
|
||||
```yaml
|
||||
test:
|
||||
stage: test
|
||||
script:
|
||||
- cd test
|
||||
- npm install
|
||||
- node api-test.js
|
||||
only:
|
||||
- main
|
||||
- develop
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 快速开始检查清单
|
||||
|
||||
- [ ] 修改 `test/api-test.js` 中的 baseURL 和测试账号
|
||||
- [ ] 安装 axios: `npm install axios`
|
||||
- [ ] 运行测试: `node test/api-test.js`
|
||||
- [ ] 检查测试报告
|
||||
- [ ] 如有失败,查看错误详情
|
||||
- [ ] 修复问题后重新测试
|
||||
|
||||
---
|
||||
|
||||
## 7. 常见问题
|
||||
|
||||
### Q1: 如何获取测试账号?
|
||||
A: 联系后端开发人员创建测试账号,或使用已有的开发账号。
|
||||
|
||||
### Q2: 测试失败怎么办?
|
||||
A:
|
||||
1. 检查API地址是否正确
|
||||
2. 检查网络连接
|
||||
3. 检查Token是否过期
|
||||
4. 查看具体错误信息
|
||||
5. 联系后端开发人员
|
||||
|
||||
### Q3: 如何测试特定功能模块?
|
||||
A: 修改 `api-test.js`,注释掉不需要测试的部分。
|
||||
|
||||
### Q4: 能否测试小程序端?
|
||||
A: 本测试方案主要测试后端API,前端页面需要使用UniApp开发工具或真机测试。
|
||||
|
||||
---
|
||||
|
||||
## 8. 联系与支持
|
||||
|
||||
- 技术文档: 参考 `API对接方案.md`
|
||||
- 问题反馈: 提交到项目Issue
|
||||
- 紧急联系: 联系项目负责人
|
||||
|
||||
---
|
||||
|
||||
**最后更新**: 2025-12-11
|
||||
285
test/api-test-collection.json
Normal file
285
test/api-test-collection.json
Normal file
@@ -0,0 +1,285 @@
|
||||
{
|
||||
"name": "武术比赛报名系统API测试集合",
|
||||
"description": "完整的API接口测试用例",
|
||||
"baseUrl": "{{baseUrl}}",
|
||||
"variables": {
|
||||
"baseUrl": "http://your-api-domain.com",
|
||||
"token": "",
|
||||
"testCompetitionId": "",
|
||||
"testAthleteId": "",
|
||||
"testRegistrationId": ""
|
||||
},
|
||||
"testCases": [
|
||||
{
|
||||
"name": "1. 用户登录",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "/martial/auth/login",
|
||||
"body": {
|
||||
"username": "test_user",
|
||||
"password": "test_password"
|
||||
}
|
||||
},
|
||||
"assertions": [
|
||||
{
|
||||
"type": "status",
|
||||
"expected": 200
|
||||
},
|
||||
{
|
||||
"type": "jsonPath",
|
||||
"path": "$.code",
|
||||
"expected": 200
|
||||
},
|
||||
{
|
||||
"type": "jsonPath",
|
||||
"path": "$.data.token",
|
||||
"saveAs": "token"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "2. 获取轮播图列表",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "/martial/banner/list",
|
||||
"headers": {
|
||||
"Blade-Auth": "Bearer {{token}}"
|
||||
}
|
||||
},
|
||||
"assertions": [
|
||||
{
|
||||
"type": "status",
|
||||
"expected": 200
|
||||
},
|
||||
{
|
||||
"type": "jsonPath",
|
||||
"path": "$.code",
|
||||
"expected": 200
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "3. 获取赛事列表",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "/martial/competition/list",
|
||||
"params": {
|
||||
"current": 1,
|
||||
"size": 20
|
||||
},
|
||||
"headers": {
|
||||
"Blade-Auth": "Bearer {{token}}"
|
||||
}
|
||||
},
|
||||
"assertions": [
|
||||
{
|
||||
"type": "status",
|
||||
"expected": 200
|
||||
},
|
||||
{
|
||||
"type": "jsonPath",
|
||||
"path": "$.data.records[0].id",
|
||||
"saveAs": "testCompetitionId"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "4. 获取赛事详情",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "/martial/competition/detail",
|
||||
"params": {
|
||||
"id": "{{testCompetitionId}}"
|
||||
},
|
||||
"headers": {
|
||||
"Blade-Auth": "Bearer {{token}}"
|
||||
}
|
||||
},
|
||||
"assertions": [
|
||||
{
|
||||
"type": "status",
|
||||
"expected": 200
|
||||
},
|
||||
{
|
||||
"type": "jsonPath",
|
||||
"path": "$.data.id",
|
||||
"expected": "{{testCompetitionId}}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "5. 获取选手列表",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "/martial/athlete/list",
|
||||
"params": {
|
||||
"current": 1,
|
||||
"size": 100
|
||||
},
|
||||
"headers": {
|
||||
"Blade-Auth": "Bearer {{token}}"
|
||||
}
|
||||
},
|
||||
"assertions": [
|
||||
{
|
||||
"type": "status",
|
||||
"expected": 200
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "6. 新增选手",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "/martial/athlete/submit",
|
||||
"headers": {
|
||||
"Blade-Auth": "Bearer {{token}}"
|
||||
},
|
||||
"body": {
|
||||
"name": "测试选手",
|
||||
"idCard": "110101199001011234",
|
||||
"team": "测试队伍",
|
||||
"gender": "男",
|
||||
"phone": "13800138000"
|
||||
}
|
||||
},
|
||||
"assertions": [
|
||||
{
|
||||
"type": "status",
|
||||
"expected": 200
|
||||
},
|
||||
{
|
||||
"type": "jsonPath",
|
||||
"path": "$.data.id",
|
||||
"saveAs": "testAthleteId"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "7. 获取报名项目列表",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "/martial/project/list",
|
||||
"params": {
|
||||
"competitionId": "{{testCompetitionId}}"
|
||||
},
|
||||
"headers": {
|
||||
"Blade-Auth": "Bearer {{token}}"
|
||||
}
|
||||
},
|
||||
"assertions": [
|
||||
{
|
||||
"type": "status",
|
||||
"expected": 200
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "8. 提交报名",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "/martial/registration/submit",
|
||||
"headers": {
|
||||
"Blade-Auth": "Bearer {{token}}"
|
||||
},
|
||||
"body": {
|
||||
"competitionId": "{{testCompetitionId}}",
|
||||
"projectIds": ["项目ID"],
|
||||
"athleteIds": ["{{testAthleteId}}"],
|
||||
"contactPhone": "13800138000",
|
||||
"totalAmount": 100
|
||||
}
|
||||
},
|
||||
"assertions": [
|
||||
{
|
||||
"type": "status",
|
||||
"expected": 200
|
||||
},
|
||||
{
|
||||
"type": "jsonPath",
|
||||
"path": "$.data.id",
|
||||
"saveAs": "testRegistrationId"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "9. 获取我的报名列表",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "/martial/registration/list",
|
||||
"params": {
|
||||
"current": 1,
|
||||
"size": 20
|
||||
},
|
||||
"headers": {
|
||||
"Blade-Auth": "Bearer {{token}}"
|
||||
}
|
||||
},
|
||||
"assertions": [
|
||||
{
|
||||
"type": "status",
|
||||
"expected": 200
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "10. 获取成绩列表",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "/martial/result/list",
|
||||
"params": {
|
||||
"competitionId": "{{testCompetitionId}}",
|
||||
"current": 1,
|
||||
"size": 100
|
||||
},
|
||||
"headers": {
|
||||
"Blade-Auth": "Bearer {{token}}"
|
||||
}
|
||||
},
|
||||
"assertions": [
|
||||
{
|
||||
"type": "status",
|
||||
"expected": 200
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "11. 获取奖牌榜",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "/martial/medal/list",
|
||||
"params": {
|
||||
"competitionId": "{{testCompetitionId}}"
|
||||
},
|
||||
"headers": {
|
||||
"Blade-Auth": "Bearer {{token}}"
|
||||
}
|
||||
},
|
||||
"assertions": [
|
||||
{
|
||||
"type": "status",
|
||||
"expected": 200
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "12. 删除选手(清理测试数据)",
|
||||
"request": {
|
||||
"method": "DELETE",
|
||||
"url": "/martial/athlete/remove",
|
||||
"params": {
|
||||
"ids": "{{testAthleteId}}"
|
||||
},
|
||||
"headers": {
|
||||
"Blade-Auth": "Bearer {{token}}"
|
||||
}
|
||||
},
|
||||
"assertions": [
|
||||
{
|
||||
"type": "status",
|
||||
"expected": 200
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
418
test/api-test.js
Normal file
418
test/api-test.js
Normal file
@@ -0,0 +1,418 @@
|
||||
/**
|
||||
* 武术比赛报名系统API自动化测试脚本
|
||||
* 运行方式:node test/api-test.js
|
||||
* 需要先安装axios: npm install axios
|
||||
*/
|
||||
|
||||
const axios = require('axios');
|
||||
|
||||
// 配置
|
||||
const config = {
|
||||
baseURL: 'http://your-api-domain.com', // 修改为你的API地址
|
||||
timeout: 10000,
|
||||
testUser: {
|
||||
username: 'test_user',
|
||||
password: 'test_password'
|
||||
}
|
||||
};
|
||||
|
||||
// 创建axios实例
|
||||
const api = axios.create({
|
||||
baseURL: config.baseURL,
|
||||
timeout: config.timeout,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
// 测试结果统计
|
||||
const testResults = {
|
||||
total: 0,
|
||||
passed: 0,
|
||||
failed: 0,
|
||||
errors: []
|
||||
};
|
||||
|
||||
// 全局变量存储测试数据
|
||||
let token = '';
|
||||
let testCompetitionId = '';
|
||||
let testAthleteId = '';
|
||||
let testRegistrationId = '';
|
||||
|
||||
// 工具函数:断言
|
||||
function assert(condition, testName, message) {
|
||||
testResults.total++;
|
||||
if (condition) {
|
||||
testResults.passed++;
|
||||
console.log(`✓ ${testName}`);
|
||||
return true;
|
||||
} else {
|
||||
testResults.failed++;
|
||||
console.error(`✗ ${testName}: ${message}`);
|
||||
testResults.errors.push({ test: testName, message });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 工具函数:设置Token
|
||||
function setToken(newToken) {
|
||||
token = newToken;
|
||||
api.defaults.headers['Blade-Auth'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
// 测试用例
|
||||
const tests = {
|
||||
// 1. 用户登录测试
|
||||
async testLogin() {
|
||||
console.log('\n【测试1】用户登录');
|
||||
try {
|
||||
const res = await api.post('/martial/auth/login', config.testUser);
|
||||
assert(res.status === 200, '登录请求HTTP状态码', `期望200,实际${res.status}`);
|
||||
assert(res.data.code === 200, '登录业务状态码', `期望200,实际${res.data.code}`);
|
||||
assert(res.data.data && res.data.data.token, '返回Token', '未返回token');
|
||||
|
||||
if (res.data.data && res.data.data.token) {
|
||||
setToken(res.data.data.token);
|
||||
console.log(` Token: ${token.substring(0, 20)}...`);
|
||||
}
|
||||
return true;
|
||||
} catch (err) {
|
||||
assert(false, '登录请求', err.message);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
// 2. 获取轮播图列表
|
||||
async testBannerList() {
|
||||
console.log('\n【测试2】获取轮播图列表');
|
||||
try {
|
||||
const res = await api.get('/martial/banner/list');
|
||||
assert(res.status === 200, '轮播图请求HTTP状态码', `期望200,实际${res.status}`);
|
||||
assert(res.data.code === 200, '轮播图业务状态码', `期望200,实际${res.data.code}`);
|
||||
assert(Array.isArray(res.data.data), '返回数据格式', '期望数组格式');
|
||||
console.log(` 轮播图数量: ${res.data.data ? res.data.data.length : 0}`);
|
||||
} catch (err) {
|
||||
assert(false, '轮播图请求', err.message);
|
||||
}
|
||||
},
|
||||
|
||||
// 3. 获取赛事列表
|
||||
async testCompetitionList() {
|
||||
console.log('\n【测试3】获取赛事列表');
|
||||
try {
|
||||
const res = await api.get('/martial/competition/list', {
|
||||
params: { current: 1, size: 20 }
|
||||
});
|
||||
assert(res.status === 200, '赛事列表HTTP状态码', `期望200,实际${res.status}`);
|
||||
assert(res.data.code === 200, '赛事列表业务状态码', `期望200,实际${res.data.code}`);
|
||||
|
||||
const hasRecords = res.data.data && res.data.data.records && res.data.data.records.length > 0;
|
||||
const isArray = Array.isArray(res.data.data) && res.data.data.length > 0;
|
||||
|
||||
assert(hasRecords || isArray, '返回赛事数据', '未返回赛事数据');
|
||||
|
||||
if (hasRecords) {
|
||||
testCompetitionId = res.data.data.records[0].id;
|
||||
console.log(` 赛事数量: ${res.data.data.records.length}`);
|
||||
console.log(` 测试赛事ID: ${testCompetitionId}`);
|
||||
} else if (isArray) {
|
||||
testCompetitionId = res.data.data[0].id;
|
||||
console.log(` 赛事数量: ${res.data.data.length}`);
|
||||
console.log(` 测试赛事ID: ${testCompetitionId}`);
|
||||
}
|
||||
} catch (err) {
|
||||
assert(false, '赛事列表请求', err.message);
|
||||
}
|
||||
},
|
||||
|
||||
// 4. 获取赛事详情
|
||||
async testCompetitionDetail() {
|
||||
console.log('\n【测试4】获取赛事详情');
|
||||
if (!testCompetitionId) {
|
||||
console.log(' 跳过:未获取到测试赛事ID');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await api.get('/martial/competition/detail', {
|
||||
params: { id: testCompetitionId }
|
||||
});
|
||||
assert(res.status === 200, '赛事详情HTTP状态码', `期望200,实际${res.status}`);
|
||||
assert(res.data.code === 200, '赛事详情业务状态码', `期望200,实际${res.data.code}`);
|
||||
assert(res.data.data && res.data.data.id, '返回赛事详情', '未返回详情数据');
|
||||
|
||||
if (res.data.data) {
|
||||
console.log(` 赛事名称: ${res.data.data.name || res.data.data.title || '未知'}`);
|
||||
console.log(` 赛事地点: ${res.data.data.location || res.data.data.address || '未知'}`);
|
||||
}
|
||||
} catch (err) {
|
||||
assert(false, '赛事详情请求', err.message);
|
||||
}
|
||||
},
|
||||
|
||||
// 5. 获取选手列表
|
||||
async testAthleteList() {
|
||||
console.log('\n【测试5】获取选手列表');
|
||||
try {
|
||||
const res = await api.get('/martial/athlete/list', {
|
||||
params: { current: 1, size: 100 }
|
||||
});
|
||||
assert(res.status === 200, '选手列表HTTP状态码', `期望200,实际${res.status}`);
|
||||
assert(res.data.code === 200, '选手列表业务状态码', `期望200,实际${res.data.code}`);
|
||||
|
||||
const records = res.data.data && res.data.data.records ? res.data.data.records.length : 0;
|
||||
const arrayLength = Array.isArray(res.data.data) ? res.data.data.length : 0;
|
||||
console.log(` 选手数量: ${records || arrayLength}`);
|
||||
} catch (err) {
|
||||
assert(false, '选手列表请求', err.message);
|
||||
}
|
||||
},
|
||||
|
||||
// 6. 新增选手
|
||||
async testCreateAthlete() {
|
||||
console.log('\n【测试6】新增选手');
|
||||
try {
|
||||
const res = await api.post('/martial/athlete/submit', {
|
||||
name: '测试选手_' + Date.now(),
|
||||
idCard: '110101199001011234',
|
||||
team: '测试队伍',
|
||||
gender: '男',
|
||||
phone: '13800138000'
|
||||
});
|
||||
assert(res.status === 200, '新增选手HTTP状态码', `期望200,实际${res.status}`);
|
||||
assert(res.data.code === 200, '新增选手业务状态码', `期望200,实际${res.data.code}`);
|
||||
|
||||
if (res.data.data && res.data.data.id) {
|
||||
testAthleteId = res.data.data.id;
|
||||
console.log(` 新增选手ID: ${testAthleteId}`);
|
||||
} else if (res.data.data) {
|
||||
testAthleteId = res.data.data;
|
||||
console.log(` 新增选手ID: ${testAthleteId}`);
|
||||
}
|
||||
} catch (err) {
|
||||
assert(false, '<27><>增选手请求', err.message);
|
||||
}
|
||||
},
|
||||
|
||||
// 7. 获取选手详情
|
||||
async testAthleteDetail() {
|
||||
console.log('\n【测试7】获取选手详情');
|
||||
if (!testAthleteId) {
|
||||
console.log(' 跳过:未获取到测试选手ID');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await api.get('/martial/athlete/detail', {
|
||||
params: { id: testAthleteId }
|
||||
});
|
||||
assert(res.status === 200, '选手详情HTTP状态码', `期望200,实际${res.status}`);
|
||||
assert(res.data.code === 200, '选手详情业务状态码', `期望200,实际${res.data.code}`);
|
||||
assert(res.data.data && res.data.data.name, '返回选手详情', '未返回详情数据');
|
||||
|
||||
if (res.data.data) {
|
||||
console.log(` 选手姓名: ${res.data.data.name}`);
|
||||
}
|
||||
} catch (err) {
|
||||
assert(false, '选手详情请求', err.message);
|
||||
}
|
||||
},
|
||||
|
||||
// 8. 获取报名项目列表
|
||||
async testProjectList() {
|
||||
console.log('\n【测试8】获取报名项目列表');
|
||||
if (!testCompetitionId) {
|
||||
console.log(' 跳过:未获取到测试赛事ID');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await api.get('/martial/project/list', {
|
||||
params: { competitionId: testCompetitionId }
|
||||
});
|
||||
assert(res.status === 200, '项目列表HTTP状态码', `期望200,实际${res.status}`);
|
||||
assert(res.data.code === 200, '项目列表业务状态码', `期望200,实际${res.data.code}`);
|
||||
|
||||
const records = res.data.data && res.data.data.records ? res.data.data.records.length : 0;
|
||||
const arrayLength = Array.isArray(res.data.data) ? res.data.data.length : 0;
|
||||
console.log(` 项目数量: ${records || arrayLength}`);
|
||||
} catch (err) {
|
||||
assert(false, '项目列表请求', err.message);
|
||||
}
|
||||
},
|
||||
|
||||
// 9. 获取我的报名列表
|
||||
async testRegistrationList() {
|
||||
console.log('\n【测试9】获取我的报名列表');
|
||||
try {
|
||||
const res = await api.get('/martial/registration/list', {
|
||||
params: { current: 1, size: 20 }
|
||||
});
|
||||
assert(res.status === 200, '报名列表HTTP状态码', `期望200,实际${res.status}`);
|
||||
assert(res.data.code === 200, '报名列表业务状态码', `期望200,实际${res.data.code}`);
|
||||
|
||||
const records = res.data.data && res.data.data.records ? res.data.data.records.length : 0;
|
||||
const arrayLength = Array.isArray(res.data.data) ? res.data.data.length : 0;
|
||||
console.log(` 报名记录数量: ${records || arrayLength}`);
|
||||
} catch (err) {
|
||||
assert(false, '报名列表请求', err.message);
|
||||
}
|
||||
},
|
||||
|
||||
// 10. 获取用户信息
|
||||
async testUserInfo() {
|
||||
console.log('\n【测试10】获取用户信息');
|
||||
try {
|
||||
const res = await api.get('/martial/user/info');
|
||||
assert(res.status === 200, '用户信息HTTP状态码', `期望200,实际${res.status}`);
|
||||
assert(res.data.code === 200, '用户信息业务状态码', `期望200,实际${res.data.code}`);
|
||||
assert(res.data.data, '返回用户信息', '未返回用户数据');
|
||||
|
||||
if (res.data.data) {
|
||||
console.log(` 用户名: ${res.data.data.name || res.data.data.username || '未知'}`);
|
||||
}
|
||||
} catch (err) {
|
||||
assert(false, '用户信息请求', err.message);
|
||||
}
|
||||
},
|
||||
|
||||
// 11. 获取赛事信息公告
|
||||
async testInfoList() {
|
||||
console.log('\n【测试11】获取赛事信息公告');
|
||||
if (!testCompetitionId) {
|
||||
console.log(' 跳过:未获取到测试赛事ID');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await api.get('/martial/info/list', {
|
||||
params: { competitionId: testCompetitionId }
|
||||
});
|
||||
assert(res.status === 200, '信息公告HTTP状态码', `期望200,实际${res.status}`);
|
||||
assert(res.data.code === 200, '信息公告业务状态码', `期望200,实际${res.data.code}`);
|
||||
|
||||
const records = res.data.data && res.data.data.records ? res.data.data.records.length : 0;
|
||||
const arrayLength = Array.isArray(res.data.data) ? res.data.data.length : 0;
|
||||
console.log(` 公告数量: ${records || arrayLength}`);
|
||||
} catch (err) {
|
||||
assert(false, '信息公告请求', err.message);
|
||||
}
|
||||
},
|
||||
|
||||
// 12. 获取成绩列表
|
||||
async testResultList() {
|
||||
console.log('\n【测试12】获取成绩列表');
|
||||
if (!testCompetitionId) {
|
||||
console.log(' 跳过:未获取到测试赛事ID');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await api.get('/martial/result/list', {
|
||||
params: { competitionId: testCompetitionId, current: 1, size: 100 }
|
||||
});
|
||||
assert(res.status === 200, '成绩列表HTTP状态码', `期望200,实际${res.status}`);
|
||||
assert(res.data.code === 200, '成绩列表业务状态码', `期望200,实际${res.data.code}`);
|
||||
|
||||
const records = res.data.data && res.data.data.records ? res.data.data.records.length : 0;
|
||||
const arrayLength = Array.isArray(res.data.data) ? res.data.data.length : 0;
|
||||
console.log(` 成绩记录数量: ${records || arrayLength}`);
|
||||
} catch (err) {
|
||||
assert(false, '成绩列表请求', err.message);
|
||||
}
|
||||
},
|
||||
|
||||
// 13. 获取奖牌榜
|
||||
async testMedalsList() {
|
||||
console.log('\n【测试13】获取奖牌榜');
|
||||
if (!testCompetitionId) {
|
||||
console.log(' 跳过:未获取到测试赛事ID');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await api.get('/martial/medal/list', {
|
||||
params: { competitionId: testCompetitionId }
|
||||
});
|
||||
assert(res.status === 200, '奖牌榜HTTP状态码', `期望200,实际${res.status}`);
|
||||
assert(res.data.code === 200, '奖牌榜业务状态码', `期望200,实际${res.data.code}`);
|
||||
|
||||
const records = res.data.data && res.data.data.records ? res.data.data.records.length : 0;
|
||||
const arrayLength = Array.isArray(res.data.data) ? res.data.data.length : 0;
|
||||
console.log(` 奖牌榜队伍数: ${records || arrayLength}`);
|
||||
} catch (err) {
|
||||
assert(false, '奖牌榜请求', err.message);
|
||||
}
|
||||
},
|
||||
|
||||
// 14. 删除测试选手(清理数据)
|
||||
async testDeleteAthlete() {
|
||||
console.log('\n【测试14】删除测试选手(清理数据)');
|
||||
if (!testAthleteId) {
|
||||
console.log(' 跳过:未创建测试选手');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await api.delete('/martial/athlete/remove', {
|
||||
params: { ids: testAthleteId }
|
||||
});
|
||||
assert(res.status === 200, '删除选手HTTP状态码', `期望200,实际${res.status}`);
|
||||
assert(res.data.code === 200, '删除选手业务状态码', `期望200,实际${res.data.code}`);
|
||||
console.log(` 成功删除测试选手: ${testAthleteId}`);
|
||||
} catch (err) {
|
||||
assert(false, '删除选手请求', err.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 运行所有测试
|
||||
async function runAllTests() {
|
||||
console.log('='.repeat(60));
|
||||
console.log('武术比赛报名系统 - API自动化测试');
|
||||
console.log('='.repeat(60));
|
||||
console.log(`测试开始时间: ${new Date().toLocaleString()}`);
|
||||
console.log(`API地址: ${config.baseURL}`);
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
// 按顺序执行所有测试
|
||||
for (const [name, testFn] of Object.entries(tests)) {
|
||||
try {
|
||||
await testFn();
|
||||
} catch (err) {
|
||||
console.error(`\n测试执行异常 [${name}]:`, err.message);
|
||||
}
|
||||
}
|
||||
|
||||
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
|
||||
|
||||
// 输出测试结果
|
||||
console.log('\n' + '='.repeat(60));
|
||||
console.log('测试结果汇总');
|
||||
console.log('='.repeat(60));
|
||||
console.log(`总测试数: ${testResults.total}`);
|
||||
console.log(`通过: ${testResults.passed} ✓`);
|
||||
console.log(`失败: ${testResults.failed} ✗`);
|
||||
console.log(`成功率: ${testResults.total > 0 ? ((testResults.passed / testResults.total) * 100).toFixed(2) : 0}%`);
|
||||
console.log(`耗时: ${duration}秒`);
|
||||
console.log(`测试结束时间: ${new Date().toLocaleString()}`);
|
||||
|
||||
if (testResults.failed > 0) {
|
||||
console.log('\n失败的测试:');
|
||||
testResults.errors.forEach((err, index) => {
|
||||
console.log(`${index + 1}. ${err.test}: ${err.message}`);
|
||||
});
|
||||
}
|
||||
|
||||
console.log('='.repeat(60));
|
||||
|
||||
// 退出进程,返回状态码
|
||||
process.exit(testResults.failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
// 执行测试
|
||||
runAllTests().catch(err => {
|
||||
console.error('测试执行失败:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
25
test/package.json
Normal file
25
test/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "martial-mini-api-test",
|
||||
"version": "1.0.0",
|
||||
"description": "武术比赛报名系统API自动化测试",
|
||||
"main": "api-test.js",
|
||||
"scripts": {
|
||||
"test": "node api-test.js",
|
||||
"test:quick": "node quick-test.js",
|
||||
"test:full": "node api-test.js",
|
||||
"test:watch": "nodemon api-test.js"
|
||||
},
|
||||
"keywords": [
|
||||
"api",
|
||||
"test",
|
||||
"automation"
|
||||
],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.0.1"
|
||||
}
|
||||
}
|
||||
113
test/quick-test.js
Normal file
113
test/quick-test.js
Normal file
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* 快速测试脚本 - 只测试核心接口
|
||||
* 运行方式:npm run test:quick
|
||||
*/
|
||||
|
||||
const axios = require('axios');
|
||||
|
||||
// 配置
|
||||
const config = {
|
||||
baseURL: 'http://your-api-domain.com', // ⚠️ 修改为你的API地址
|
||||
testUser: {
|
||||
username: 'test_user', // ⚠️ 修改为测试账号
|
||||
password: 'test_password' // ⚠️ 修改为测试密码
|
||||
}
|
||||
};
|
||||
|
||||
let passCount = 0;
|
||||
let failCount = 0;
|
||||
|
||||
function log(emoji, message) {
|
||||
console.log(`${emoji} ${message}`);
|
||||
}
|
||||
|
||||
async function quickTest() {
|
||||
console.log('\n🚀 武术比赛报名系统 - 快速测试\n');
|
||||
console.log(`📍 API地址: ${config.baseURL}\n`);
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: config.baseURL,
|
||||
timeout: 10000,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
|
||||
try {
|
||||
// 测试1:登录
|
||||
log('🔐', '测试登录...');
|
||||
const loginRes = await api.post('/martial/auth/login', config.testUser);
|
||||
if (loginRes.data.code === 200 && loginRes.data.data.token) {
|
||||
log('✅', '登录成功');
|
||||
passCount++;
|
||||
const token = loginRes.data.data.token;
|
||||
api.defaults.headers['Blade-Auth'] = `Bearer ${token}`;
|
||||
} else {
|
||||
log('❌', '登录失败');
|
||||
failCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
// 测试2:赛事列表
|
||||
log('📋', '测试赛事列表...');
|
||||
const compRes = await api.get('/martial/competition/list?current=1&size=10');
|
||||
if (compRes.data.code === 200) {
|
||||
const count = compRes.data.data.records?.length || compRes.data.data?.length || 0;
|
||||
log('✅', `赛事列表获取成功 (${count}条数据)`);
|
||||
passCount++;
|
||||
} else {
|
||||
log('❌', '赛事列表获取失败');
|
||||
failCount++;
|
||||
}
|
||||
|
||||
// 测试3:选手列表
|
||||
log('👥', '测试选手列表...');
|
||||
const athleteRes = await api.get('/martial/athlete/list?current=1&size=10');
|
||||
if (athleteRes.data.code === 200) {
|
||||
const count = athleteRes.data.data.records?.length || athleteRes.data.data?.length || 0;
|
||||
log('✅', `选手列表获取成功 (${count}条数据)`);
|
||||
passCount++;
|
||||
} else {
|
||||
log('❌', '选手列表获取失败');
|
||||
failCount++;
|
||||
}
|
||||
|
||||
// 测试4:用户信息
|
||||
log('👤', '测试用户信息...');
|
||||
const userRes = await api.get('/martial/user/info');
|
||||
if (userRes.data.code === 200) {
|
||||
log('✅', `用户信息获取成功 (${userRes.data.data.name || '未知'})`);
|
||||
passCount++;
|
||||
} else {
|
||||
log('❌', '用户信息获取失败');
|
||||
failCount++;
|
||||
}
|
||||
|
||||
// 测试5:报名列表
|
||||
log('📝', '测试报名列表...');
|
||||
const regRes = await api.get('/martial/registration/list?current=1&size=10');
|
||||
if (regRes.data.code === 200) {
|
||||
const count = regRes.data.data.records?.length || regRes.data.data?.length || 0;
|
||||
log('✅', `报名列表获取成功 (${count}条数据)`);
|
||||
passCount++;
|
||||
} else {
|
||||
log('❌', '报名列表获取失败');
|
||||
failCount++;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
log('❌', `请求失败: ${error.message}`);
|
||||
failCount++;
|
||||
}
|
||||
|
||||
// 输出结果
|
||||
console.log('\n' + '='.repeat(50));
|
||||
console.log(`📊 测试结果: ${passCount}个通过, ${failCount}个失败`);
|
||||
console.log(`✨ 成功率: ${((passCount / (passCount + failCount)) * 100).toFixed(1)}%`);
|
||||
console.log('='.repeat(50) + '\n');
|
||||
|
||||
process.exit(failCount > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
quickTest().catch(err => {
|
||||
console.error('❌ 测试执行失败:', err.message);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user