10.5--商品与秒杀活动测试
测试前准备
1. MySQL 已启动,数据库 secgo_mall 已创建
2. Redis 已启动(redis-server 或 Docker)
3. 服务已启动:go run cmd/secgo-mall/main.go
4. 安装Postman与ab/hey,并了解如何操作(这里选用hey)
测试工具推荐
| 工具 | 用途 |
|---|---|
ab (Apache Bench) |
简单并发测试 |
wrk |
高性能 HTTP 压测 |
redis-cli |
手动验证 Redis 状态 |
| Postman | 单接口功能测试 |
hey(原weighttp) |
比ab更适合windows系统 |
# hey安装
go install github.com/rakyll/hey@latest
测试一:商品模块
Step 1: 创建商品
POST http://localhost:8080/api/product/create
Headers: Authorization: Bearer <TOKEN>
Body: {
"name": "iPhone 16",
"description": "苹果旗舰手机",
"price": 8999.00,
"stock": 100
}
预期:返回商品信息,包含 product_id
Step 2: 查看商品列表
GET http://localhost:8080/api/product/list
Headers: Authorization: Bearer <TOKEN>
预期:返回包含刚才创建商品的列表
step 1:
刷新数据库可以看到product表中出现iPhone 16

step 2:
这里我多创建了一个iPhone 16 Pro商品,可以看到返回Json中带有列表中所有商品

测试二:秒杀活动模块
Step 1: 创建秒杀活动
POST http://localhost:8080/api/seckill/create
Headers: Authorization: Bearer <TOKEN>
Body: {
"product_id": 1,
"name": "iPhone 16 限时秒杀",
"price": 1.00,
"stock": 10,
"start_time": 1775966400,
"end_time": 1776657600
}
注意:start_time 和 end_time 为 Unix 时间戳(秒)
start_time 为 2026-04-12 12:00:00
end_time 为 2026-04-20 12:00:00
预期:返回秒杀活动信息,status=0
Step 2: 验证 Redis 库存预热
redis-cli
> GET seckill:stock:1
"10"
预期:库存已写入 Redis
Step 3: 查看进行中的秒杀活动
GET http://localhost:8080/api/seckill/list/active
Headers: Authorization: Bearer <TOKEN>
预期:返回活动列表(当前 start_time 需设为当前时间之前才显示)
step 1:

step 2:
可以看到秒杀活动完成了商品库存位于Redis中的预热

step 3:
正在进行(status == 1)的秒杀只有两个

测试三:抢购模块
Step 1: 抢购(正常)
POST http://localhost:8080/api/seckill/purchase
Headers: Authorization: Bearer <TOKEN>
Body: {"seckill_id": 2}
预期:{"code": 0, "message": "success", "data": {"message": "purchase success, order is being processed"}}
Step 2: 验证 Redis 库存减少
redis-cli
> GET seckill:stock:1
"9"
预期:库存从 10 减到 9
Step 3: 库存耗尽后再抢
重复 Step 1,直到库存为 0
> GET seckill:stock:1
"0"
Step 4: 库存为 0 后再抢
POST http://localhost:8080/api/seckill/purchase
Headers: Authorization: Bearer <TOKEN>
Body: {"seckill_id": 1}
预期:{"code": 0, "message": "stock not enough"}
step 1
由于尚未加入消息队列,抢购成功后并不会更新MySQL数据表中stock的数据

step 2
缓存中的stock已经顺利更新

step 3
在Postman中再send九次之后

step 4
在Postman中再次send一次,顺利报错

测试四:活动状态校验
场景 A: 活动未开始(start_time > now)
POST http://localhost:8080/api/seckill/purchase
Body: {"seckill_id": 活动id}
预期:{"code": -2, "message": "seckill not started"}
场景 B: 活动已结束(end_time < now)
POST http://localhost:8080/api/seckill/purchase
Body: {"seckill_id": 活动id}
预期:{"code": -3, "message": "seckill ended"}
准备工作
我们先创建一个新商品和两个活动(一个未开始与一个已结束)
POST http://localhost:8080/api/product/create
Headers: Authorization: Bearer <TOKEN>
Body: {
"name": "Xiaomi 17",
"description": "小米旗舰手机",
"price": 4499.00,
"stock": 100
}



step A:

step B:

测试五:高并发测试(有/无 Redis 对比)
事先准备: 我们需要创建两个进行中的秒杀活动
这里我们选择商品3来进行创建,同时秒杀库存设置为100
注: 我在DataGrip将product表中的商品3库存修改为了500,Update的api我会在后续进行补充
A组
{
"product_id": 3,
"name": "Xiaomi 17 限时秒杀(秒杀测试)",
"price": 1.00,
"stock": 100,
"start_time": 1775786400,
"end_time": 1776391200
}
创建成功:
{
"code": 0,
"message": "success",
"data": {
"seckill_id": 7,
"product_id": 3,
"name": "Xiaomi 17 限时秒杀(有Redis测试)",
"price": 1,
"stock": 100,
"start_time": "2026-04-10T10:00:00+08:00",
"end_time": "2026-04-17T10:00:00+08:00",
"status": 1,
"created_at": "2026-04-14T11:55:30.311+08:00"
}
}
B组
{
"product_id": 3,
"name": "Xiaomi 17 限时秒杀(无Redis测试)",
"price": 1.00,
"stock": 100,
"start_time": 1775786400,
"end_time": 1776391200
}
创建成功:
{
"code": 0,
"message": "success",
"data": {
"seckill_id": 8,
"product_id": 3,
"name": "Xiaomi 17 限时秒杀(无Redis测试)",
"price": 1,
"stock": 100,
"start_time": "2026-04-10T10:00:00+08:00",
"end_time": "2026-04-17T10:00:00+08:00",
"status": 1,
"created_at": "2026-04-14T11:56:35.715+08:00"
}
}
对照组 A:有 Redis Lua 扣减(当前实现)
# 二选一:(自行去除换行和反斜杠,并替换JSON和TOKEN)
# 用 ab 模拟 100 并发,总共 100 请求
ab -n 120 -c 120 -p purchase.json -T application/json \
http://localhost:8080/api/seckill/purchase
# 用 hey 模拟
hey -n 120 -c 120 -m POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <TOKEN>" \
-D purchase.json \
http://localhost:8080/api/seckill/purchase
# 验证
redis-cli GET seckill:stock:7
→ "0"
MySQL order 表订单数 = 100(无误)



也可以看到MySQL的order表中只创建了100份订单,而非120
![[A组order表中创建了100份订单.png]]
对照组 B:临时模拟无 Redis(直接打 MySQL)
为了保证直接扣减MySQL,我们需要对 service/seckill.go 中的 func SeckillPurchase(seckillId, userId uint) (int, error)改为下图所示:
![[SeckillPurchare修改.png|func SeckillPurchase(seckillId, userId uint) (int, error)]]
// 跳过 Redis,直接 UPDATE MySQL
tx := repository.DB.Begin()
result := tx.Model(&model.Seckill{}).
Where("seckill_id = ? AND stock > 0", seckillId).
Update("stock", gorm.Expr("stock - 1"))
tx.Commit()
if result.RowsAffected == 0 {
return 0, errors.New("stock not enough")
}
// 不发 MQ,直接返回
return 1, nil
重启main.go
# 相同并发测试
ab -n 120 -c 120 -p purchase.json -T application/json \
http://localhost:8080/api/seckill/purchase
# or
hey -n 120 -c 120 -m POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <TOKEN>" \
-D purchase.json \
http://localhost:8080/api/seckill/purchase

我们不仅能看到超卖报错的提示(stock扣减时必须>0),也可以看到没有缓存下每条抢购请求的平均耗时都显著增加
注:记得将
SeckillPurchase()修改为原样
完整测试流程清单
| 序号 | 接口 | 方法 | 验证点 |
|---|---|---|---|
| 1 | /api/product/create |
POST | 商品创建成功 |
| 2 | /api/product/list |
GET | 商品列表返回 |
| 3 | /api/seckill/create |
POST | 活动创建 + Redis 预热 |
| 4 | redis-cli GET seckill:stock:1 |
CLI | 库存预热成功 |
| 5 | /api/seckill/list |
GET | 活动列表返回 |
| 6 | /api/seckill/purchase |
POST | 抢购成功,库存-1 |
| 7 | redis-cli GET seckill:stock:1 |
CLI | 库存递减验证 |
| 8 | 多次 /api/seckill/purchase |
POST | 库存耗尽返回失败 |
| 9 | ab 高并发测试 | CLI | Lua 扣减不超卖 |

浙公网安备 33010602011771号