移除所有测试代码和测试要求
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m33s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m33s
**变更说明**: - 删除所有 *_test.go 文件(单元测试、集成测试、验收测试、流程测试) - 删除整个 tests/ 目录 - 更新 CLAUDE.md:用"测试禁令"章节替换所有测试要求 - 删除测试生成 Skill (openspec-generate-acceptance-tests) - 删除测试生成命令 (opsx:gen-tests) - 更新 tasks.md:删除所有测试相关任务 **新规范**: - ❌ 禁止编写任何形式的自动化测试 - ❌ 禁止创建 *_test.go 文件 - ❌ 禁止在任务中包含测试相关工作 - ✅ 仅当用户明确要求时才编写测试 **原因**: 业务系统的正确性通过人工验证和生产环境监控保证,测试代码维护成本高于价值。 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -111,7 +111,7 @@ Working on task 4/7: <task description>
|
||||
- [x] Task 2
|
||||
...
|
||||
|
||||
All tasks complete! Ready to archive this change.
|
||||
All tasks complete! You can archive this change with `/opsx:archive`.
|
||||
```
|
||||
|
||||
**Output On Pause (Issue Encountered)**
|
||||
|
||||
@@ -41,7 +41,7 @@ Continue working on a change by creating the next artifact.
|
||||
**If all artifacts are complete (`isComplete: true`)**:
|
||||
- Congratulate the user
|
||||
- Show final status including the schema used
|
||||
- Suggest: "All artifacts created! You can now implement this change or archive it."
|
||||
- Suggest: "All artifacts created! You can now implement this change with `/opsx:apply` or archive it with `/opsx:archive`."
|
||||
- STOP
|
||||
|
||||
---
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
---
|
||||
description: 从 Spec 的 Scenarios 和 Business Flows 自动生成验收测试和流程测试
|
||||
---
|
||||
|
||||
从 Spec 文档自动生成两类测试:
|
||||
1. **验收测试**(Acceptance Tests):从 Scenarios 生成,验证单 API 契约
|
||||
2. **流程测试**(Flow Tests):从 Business Flows 生成,验证多 API 业务场景
|
||||
|
||||
**Input**: 可选指定 change 名称(如 `/opsx:gen-tests add-auth`)。如果省略,从上下文推断或提示选择。
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **选择 change**
|
||||
|
||||
如果提供了名称,使用它。否则:
|
||||
- 从对话上下文推断
|
||||
- 如果只有一个活跃 change,自动选择
|
||||
- 如果模糊,运行 `openspec list --json` 让用户选择
|
||||
|
||||
2. **检查 change 状态**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
确认 specs artifact 已完成(`status: "done"`)
|
||||
|
||||
3. **读取 spec 文件**
|
||||
|
||||
读取 `openspec/changes/<change-name>/specs/*/spec.md` 下的所有 spec 文件。
|
||||
|
||||
4. **解析 Scenarios**
|
||||
|
||||
从每个 spec 文件中提取 `#### Scenario:` 块:
|
||||
```markdown
|
||||
#### Scenario: 成功创建套餐
|
||||
- **GIVEN** 用户已登录且有创建权限
|
||||
- **WHEN** POST /api/admin/packages with valid data
|
||||
- **THEN** 返回 200 和套餐详情
|
||||
```
|
||||
|
||||
5. **解析 Business Flows**(如果存在)
|
||||
|
||||
从 spec 文件中提取 `### Flow:` 块,包含多步骤业务场景。
|
||||
|
||||
6. **生成验收测试**
|
||||
|
||||
输出路径:`tests/acceptance/<capability>_acceptance_test.go`
|
||||
|
||||
模板结构:
|
||||
```go
|
||||
func Test{Capability}_Acceptance(t *testing.T) {
|
||||
env := testutils.NewIntegrationTestEnv(t)
|
||||
|
||||
t.Run("Scenario_{name}", func(t *testing.T) {
|
||||
// GIVEN: ...
|
||||
// WHEN: ...
|
||||
// THEN: ...
|
||||
// 破坏点:...
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
7. **生成流程测试**
|
||||
|
||||
输出路径:`tests/flows/<capability>_<flow>_flow_test.go`
|
||||
|
||||
模板结构:
|
||||
```go
|
||||
func TestFlow_{FlowName}(t *testing.T) {
|
||||
env := testutils.NewIntegrationTestEnv(t)
|
||||
|
||||
var (
|
||||
// 流程级共享状态
|
||||
)
|
||||
|
||||
t.Run("Step1_{name}", func(t *testing.T) {
|
||||
// 依赖:...
|
||||
// 破坏点:...
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
8. **运行测试验证**
|
||||
|
||||
```bash
|
||||
source .env.local && go test -v ./tests/acceptance/... ./tests/flows/... 2>&1 | head -50
|
||||
```
|
||||
|
||||
**预期**:全部 FAIL(功能未实现,证明测试有效)
|
||||
|
||||
**如果测试 PASS**:说明测试写得太弱,需要加强
|
||||
|
||||
**Output**
|
||||
|
||||
```
|
||||
## 测试生成完成
|
||||
|
||||
**Change:** <change-name>
|
||||
**来源:** specs/<capability>/spec.md
|
||||
|
||||
### 生成的测试文件
|
||||
|
||||
**验收测试** (tests/acceptance/):
|
||||
- <capability>_acceptance_test.go
|
||||
- Scenario_xxx
|
||||
- Scenario_yyy
|
||||
|
||||
**流程测试** (tests/flows/):
|
||||
- <capability>_<flow>_flow_test.go
|
||||
- Step1_xxx
|
||||
- Step2_yyy
|
||||
|
||||
### 验证结果
|
||||
|
||||
$ source .env.local && go test -v ./tests/acceptance/... ./tests/flows/...
|
||||
|
||||
--- FAIL: TestXxx_Acceptance (0.00s)
|
||||
--- FAIL: TestXxx_Acceptance/Scenario_xxx (0.00s)
|
||||
xxx_acceptance_test.go:45: 404 != 200
|
||||
|
||||
✓ 所有测试预期 FAIL(功能未实现)
|
||||
✓ 测试生成完成
|
||||
|
||||
下一步: 开始实现 tasks,每完成一个功能单元运行相关测试验证
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
|
||||
- 每个 Scenario 必须生成一个测试用例(不要跳过)
|
||||
- 每个测试必须包含"破坏点"注释
|
||||
- 流程测试的 step 必须声明依赖
|
||||
- 使用 IntegrationTestEnv,不要 mock 依赖
|
||||
- 测试必须在功能缺失时 FAIL(不要写永远 PASS 的测试)
|
||||
- 详细模板参考:`.opencode/skills/openspec-generate-acceptance-tests/SKILL.md`
|
||||
5
.claude/settings.json
Normal file
5
.claude/settings.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"enabledPlugins": {
|
||||
"ralph-loop@claude-plugins-official": true
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.0.2"
|
||||
generatedBy: "1.1.1"
|
||||
---
|
||||
|
||||
Implement tasks from an OpenSpec change.
|
||||
|
||||
@@ -6,7 +6,7 @@ compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.0.2"
|
||||
generatedBy: "1.1.1"
|
||||
---
|
||||
|
||||
Archive a completed change in the experimental workflow.
|
||||
|
||||
@@ -6,7 +6,7 @@ compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.0.2"
|
||||
generatedBy: "1.1.1"
|
||||
---
|
||||
|
||||
Archive multiple completed changes in a single operation.
|
||||
|
||||
@@ -6,7 +6,7 @@ compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.0.2"
|
||||
generatedBy: "1.1.1"
|
||||
---
|
||||
|
||||
Continue working on a change by creating the next artifact.
|
||||
|
||||
@@ -6,7 +6,7 @@ compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.0.2"
|
||||
generatedBy: "1.1.1"
|
||||
---
|
||||
|
||||
Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
|
||||
|
||||
@@ -6,7 +6,7 @@ compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.0.2"
|
||||
generatedBy: "1.1.1"
|
||||
---
|
||||
|
||||
Fast-forward through artifact creation - generate everything needed to start implementation in one go.
|
||||
|
||||
@@ -1,442 +0,0 @@
|
||||
---
|
||||
name: openspec-generate-acceptance-tests
|
||||
description: 从 Spec 的 Scenarios 和 Business Flows 自动生成验收测试和流程测试。测试在实现前生成,预期全部 FAIL,证明测试有效。
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: junhong
|
||||
version: "1.0"
|
||||
---
|
||||
|
||||
# 测试生成 Skill
|
||||
|
||||
从 Spec 文档自动生成两类测试:
|
||||
1. **验收测试**(Acceptance Tests):从 Scenarios 生成,验证单 API 契约
|
||||
2. **流程测试**(Flow Tests):从 Business Flows 生成,验证多 API 业务场景
|
||||
|
||||
## 触发方式
|
||||
|
||||
```
|
||||
/opsx:gen-tests [change-name]
|
||||
```
|
||||
|
||||
如果不指定 change-name,自动检测当前活跃的 change。
|
||||
|
||||
---
|
||||
|
||||
## 前置条件
|
||||
|
||||
1. Change 必须存在且包含 spec 文件
|
||||
2. Spec 必须包含 `## Scenarios` 部分
|
||||
3. Spec 建议包含 `## Business Flows` 部分(如果有跨 API 场景)
|
||||
|
||||
检查命令:
|
||||
```bash
|
||||
openspec list --json
|
||||
# 确认 change 存在且有 specs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 工作流程
|
||||
|
||||
### Step 1: 读取 Spec 文件
|
||||
|
||||
```bash
|
||||
# 读取 change 的所有 spec 文件
|
||||
cat openspec/changes/<change-name>/specs/<capability>/spec.md
|
||||
```
|
||||
|
||||
### Step 2: 解析 Scenarios
|
||||
|
||||
从 Spec 中提取所有 Scenario:
|
||||
|
||||
```markdown
|
||||
#### Scenario: 成功创建套餐
|
||||
- **GIVEN** 用户已登录且有创建权限
|
||||
- **WHEN** POST /api/admin/packages with valid data
|
||||
- **THEN** 返回 201 和套餐详情
|
||||
- **AND** 数据库中存在该套餐记录
|
||||
```
|
||||
|
||||
解析为结构:
|
||||
```json
|
||||
{
|
||||
"name": "成功创建套餐",
|
||||
"given": ["用户已登录且有创建权限"],
|
||||
"when": {"method": "POST", "path": "/api/admin/packages", "condition": "valid data"},
|
||||
"then": ["返回 201 和套餐详情"],
|
||||
"and": ["数据库中存在该套餐记录"]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: 解析 Business Flows
|
||||
|
||||
从 Spec 中提取 Business Flow:
|
||||
|
||||
```markdown
|
||||
### Flow: 套餐完整生命周期
|
||||
|
||||
**参与者**: 平台管理员, 代理商
|
||||
|
||||
**流程步骤**:
|
||||
|
||||
1. **创建套餐**
|
||||
- 角色: 平台管理员
|
||||
- 调用: POST /api/admin/packages
|
||||
- 预期: 返回套餐 ID
|
||||
|
||||
2. **分配给代理商**
|
||||
- 角色: 平台管理员
|
||||
- 调用: POST /api/admin/shop-packages
|
||||
- 输入: 套餐 ID + 店铺 ID
|
||||
- 预期: 分配成功
|
||||
|
||||
3. **代理商查看可售套餐**
|
||||
- 角色: 代理商
|
||||
- 调用: GET /api/admin/shop-packages
|
||||
- 预期: 列表包含刚分配的套餐
|
||||
```
|
||||
|
||||
### Step 4: 生成验收测试
|
||||
|
||||
**输出路径**: `tests/acceptance/<capability>_acceptance_test.go`
|
||||
|
||||
```go
|
||||
package acceptance
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"junhong_cmp_fiber/tests/testutils"
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// 验收测试:套餐管理
|
||||
// 来源:openspec/changes/package-management/specs/package/spec.md
|
||||
// ============================================================
|
||||
|
||||
func TestPackage_Acceptance(t *testing.T) {
|
||||
env := testutils.NewIntegrationTestEnv(t)
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Scenario: 成功创建套餐
|
||||
// GIVEN: 用户已登录且有创建权限
|
||||
// WHEN: POST /api/admin/packages with valid data
|
||||
// THEN: 返回 201 和套餐详情
|
||||
// AND: 数据库中存在该套餐记录
|
||||
//
|
||||
// 破坏点:如果删除 handler.Create 中的 store.Create 调用,此测试将失败
|
||||
// ------------------------------------------------------------
|
||||
t.Run("Scenario_成功创建套餐", func(t *testing.T) {
|
||||
// GIVEN: 用户已登录且有创建权限
|
||||
client := env.AsSuperAdmin()
|
||||
|
||||
// WHEN: POST /api/admin/packages with valid data
|
||||
body := map[string]interface{}{
|
||||
"name": "测试套餐",
|
||||
"description": "测试描述",
|
||||
"price": 9900,
|
||||
"duration": 30,
|
||||
}
|
||||
resp, err := client.Request("POST", "/api/admin/packages", body)
|
||||
require.NoError(t, err)
|
||||
|
||||
// THEN: 返回 201 和套餐详情
|
||||
assert.Equal(t, 201, resp.StatusCode)
|
||||
|
||||
var result map[string]interface{}
|
||||
err = resp.JSON(&result)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, int(result["code"].(float64)))
|
||||
|
||||
data := result["data"].(map[string]interface{})
|
||||
packageID := uint(data["id"].(float64))
|
||||
assert.NotZero(t, packageID)
|
||||
|
||||
// AND: 数据库中存在该套餐记录
|
||||
// TODO: 实现后取消注释
|
||||
// pkg, err := env.DB().Package.FindByID(ctx, packageID)
|
||||
// require.NoError(t, err)
|
||||
// assert.Equal(t, "测试套餐", pkg.Name)
|
||||
})
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Scenario: 创建套餐参数校验失败
|
||||
// GIVEN: 用户已登录
|
||||
// WHEN: POST /api/admin/packages with invalid data (name empty)
|
||||
// THEN: 返回 400 和错误信息
|
||||
//
|
||||
// 破坏点:如果删除 handler 中的参数校验,此测试将失败
|
||||
// ------------------------------------------------------------
|
||||
t.Run("Scenario_创建套餐参数校验失败", func(t *testing.T) {
|
||||
// GIVEN: 用户已登录
|
||||
client := env.AsSuperAdmin()
|
||||
|
||||
// WHEN: POST /api/admin/packages with invalid data
|
||||
body := map[string]interface{}{
|
||||
"name": "", // 空名称
|
||||
"price": -1, // 负价格
|
||||
}
|
||||
resp, err := client.Request("POST", "/api/admin/packages", body)
|
||||
require.NoError(t, err)
|
||||
|
||||
// THEN: 返回 400 和错误信息
|
||||
assert.Equal(t, 400, resp.StatusCode)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: 生成流程测试
|
||||
|
||||
**输出路径**: `tests/flows/<capability>_<flow-name>_flow_test.go`
|
||||
|
||||
```go
|
||||
package flows
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"junhong_cmp_fiber/tests/testutils"
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// 流程测试:套餐完整生命周期
|
||||
// 来源:openspec/changes/package-management/specs/package/spec.md
|
||||
// 参与者:平台管理员, 代理商
|
||||
// ============================================================
|
||||
|
||||
func TestFlow_PackageLifecycle(t *testing.T) {
|
||||
env := testutils.NewIntegrationTestEnv(t)
|
||||
|
||||
// 流程级共享状态
|
||||
var (
|
||||
packageID uint
|
||||
shopID uint = 1 // 测试店铺 ID
|
||||
)
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Step 1: 创建套餐
|
||||
// 角色: 平台管理员
|
||||
// 调用: POST /api/admin/packages
|
||||
// 预期: 返回套餐 ID
|
||||
//
|
||||
// 破坏点:如果套餐创建 API 不返回 ID,后续步骤无法执行
|
||||
// ------------------------------------------------------------
|
||||
t.Run("Step1_平台管理员创建套餐", func(t *testing.T) {
|
||||
client := env.AsSuperAdmin()
|
||||
|
||||
body := map[string]interface{}{
|
||||
"name": "流程测试套餐",
|
||||
"description": "用于流程测试",
|
||||
"price": 19900,
|
||||
"duration": 30,
|
||||
}
|
||||
resp, err := client.Request("POST", "/api/admin/packages", body)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 201, resp.StatusCode)
|
||||
|
||||
var result map[string]interface{}
|
||||
err = resp.JSON(&result)
|
||||
require.NoError(t, err)
|
||||
|
||||
data := result["data"].(map[string]interface{})
|
||||
packageID = uint(data["id"].(float64))
|
||||
require.NotZero(t, packageID, "套餐 ID 不能为空")
|
||||
})
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Step 2: 分配给代理商
|
||||
// 角色: 平台管理员
|
||||
// 调用: POST /api/admin/shop-packages
|
||||
// 输入: 套餐 ID + 店铺 ID
|
||||
// 预期: 分配成功
|
||||
//
|
||||
// 依赖: Step 1 的 packageID
|
||||
// 破坏点:如果分配 API 不检查套餐是否存在,可能分配无效套餐
|
||||
// ------------------------------------------------------------
|
||||
t.Run("Step2_分配套餐给代理商", func(t *testing.T) {
|
||||
if packageID == 0 {
|
||||
t.Skip("依赖 Step 1 创建的 packageID")
|
||||
}
|
||||
|
||||
client := env.AsSuperAdmin()
|
||||
|
||||
body := map[string]interface{}{
|
||||
"package_id": packageID,
|
||||
"shop_id": shopID,
|
||||
}
|
||||
resp, err := client.Request("POST", "/api/admin/shop-packages", body)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.StatusCode)
|
||||
})
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Step 3: 代理商查看可售套餐
|
||||
// 角色: 代理商
|
||||
// 调用: GET /api/admin/shop-packages
|
||||
// 预期: 列表包含刚分配的套餐
|
||||
//
|
||||
// 依赖: Step 2 的分配操作
|
||||
// 破坏点:如果查询不按店铺过滤,代理商会看到其他店铺的套餐
|
||||
// ------------------------------------------------------------
|
||||
t.Run("Step3_代理商查看可售套餐", func(t *testing.T) {
|
||||
if packageID == 0 {
|
||||
t.Skip("依赖 Step 1 创建的 packageID")
|
||||
}
|
||||
|
||||
// 以代理商身份请求
|
||||
client := env.AsShopAgent(shopID)
|
||||
|
||||
resp, err := client.Request("GET", "/api/admin/shop-packages", nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.StatusCode)
|
||||
|
||||
var result map[string]interface{}
|
||||
err = resp.JSON(&result)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 验证列表包含刚分配的套餐
|
||||
data := result["data"].(map[string]interface{})
|
||||
list := data["list"].([]interface{})
|
||||
|
||||
found := false
|
||||
for _, item := range list {
|
||||
pkg := item.(map[string]interface{})
|
||||
if uint(pkg["package_id"].(float64)) == packageID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, found, "代理商应该能看到刚分配的套餐")
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### Step 6: 运行测试验证
|
||||
|
||||
生成测试后,立即运行验证:
|
||||
|
||||
```bash
|
||||
# 预期全部 FAIL(因为功能尚未实现)
|
||||
source .env.local && go test -v ./tests/acceptance/... ./tests/flows/... 2>&1 | head -50
|
||||
```
|
||||
|
||||
**如果测试 PASS**:
|
||||
- 说明测试写得太弱,没有真正验证功能
|
||||
- 需要加强测试或检查是否功能已存在
|
||||
|
||||
---
|
||||
|
||||
## 测试模板规范
|
||||
|
||||
### 验收测试必须包含
|
||||
|
||||
1. **来源注释**:标明从哪个 spec 文件生成
|
||||
2. **Scenario 注释**:完整的 GIVEN/WHEN/THEN/AND
|
||||
3. **破坏点注释**:说明什么代码变更会导致测试失败
|
||||
4. **清晰的结构**:GIVEN → WHEN → THEN → AND 分块
|
||||
|
||||
### 流程测试必须包含
|
||||
|
||||
1. **来源注释**:标明从哪个 spec 文件生成
|
||||
2. **参与者注释**:涉及哪些角色
|
||||
3. **共享状态声明**:流程中需要传递的数据
|
||||
4. **依赖声明**:每个 step 依赖哪些前置 step
|
||||
5. **破坏点注释**:说明什么代码变更会导致测试失败
|
||||
|
||||
### 破坏点注释示例
|
||||
|
||||
```go
|
||||
// 破坏点:如果删除 handler.Create 中的 store.Create 调用,此测试将失败
|
||||
// 破坏点:如果移除参数校验中的 name 必填检查,此测试将失败
|
||||
// 破坏点:如果查询不按 shop_id 过滤,此测试将失败(会返回其他店铺数据)
|
||||
// 破坏点:如果状态机允许 "已使用" → "未分配" 的转换,此测试将失败
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 输出文件
|
||||
|
||||
| 来源 | 输出 |
|
||||
|------|------|
|
||||
| Spec Scenarios | `tests/acceptance/<capability>_acceptance_test.go` |
|
||||
| Spec Business Flows | `tests/flows/<capability>_<flow>_flow_test.go` |
|
||||
|
||||
---
|
||||
|
||||
## Guardrails
|
||||
|
||||
- **不要跳过 Scenario** - 每个 Scenario 必须对应一个测试用例
|
||||
- **不要省略破坏点** - 每个测试必须有破坏点注释
|
||||
- **不要硬编码 ID** - 使用共享变量传递流程中的 ID
|
||||
- **不要忽略依赖** - 流程测试的 step 必须声明依赖
|
||||
- **不要写永远 PASS 的测试** - 测试必须在功能缺失时 FAIL
|
||||
|
||||
---
|
||||
|
||||
## 与其他 Skills 的关系
|
||||
|
||||
| Skill | 关系 |
|
||||
|-------|------|
|
||||
| `openspec-continue-change` | 生成 spec 后可触发 gen-tests |
|
||||
| `openspec-apply-change` | 实现时参考生成的测试 |
|
||||
| `openspec-verify-change` | 验证时运行生成的测试 |
|
||||
|
||||
---
|
||||
|
||||
## 示例交互
|
||||
|
||||
```
|
||||
User: /opsx:gen-tests package-management
|
||||
|
||||
AI: 正在分析 change: package-management
|
||||
|
||||
找到 spec 文件: openspec/changes/package-management/specs/package/spec.md
|
||||
|
||||
解析结果:
|
||||
- Scenarios: 5 个
|
||||
- Business Flows: 2 个
|
||||
|
||||
生成测试文件:
|
||||
|
||||
1. tests/acceptance/package_acceptance_test.go
|
||||
- Scenario_成功创建套餐
|
||||
- Scenario_创建套餐参数校验失败
|
||||
- Scenario_成功更新套餐
|
||||
- Scenario_成功删除套餐
|
||||
- Scenario_删除已分配套餐失败
|
||||
|
||||
2. tests/flows/package_lifecycle_flow_test.go
|
||||
- Step1_平台管理员创建套餐
|
||||
- Step2_分配套餐给代理商
|
||||
- Step3_代理商查看可售套餐
|
||||
- Step4_代理商创建订单
|
||||
- Step5_套餐使用统计更新
|
||||
|
||||
3. tests/flows/package_allocation_flow_test.go
|
||||
- Step1_批量分配套餐
|
||||
- Step2_验证分配结果
|
||||
- Step3_撤销部分分配
|
||||
|
||||
运行验证:
|
||||
$ source .env.local && go test -v ./tests/acceptance/package_... ./tests/flows/package_...
|
||||
|
||||
--- FAIL: TestPackage_Acceptance (0.00s)
|
||||
--- FAIL: TestPackage_Acceptance/Scenario_成功创建套餐 (0.00s)
|
||||
package_acceptance_test.go:45: 404 != 201
|
||||
--- FAIL: TestFlow_PackageLifecycle (0.00s)
|
||||
--- FAIL: TestFlow_PackageLifecycle/Step1_平台管理员创建套餐 (0.00s)
|
||||
package_lifecycle_flow_test.go:38: 404 != 201
|
||||
|
||||
✓ 所有测试预期 FAIL(功能未实现)
|
||||
✓ 测试生成完成
|
||||
|
||||
下一步: /opsx:continue 生成 design 和 tasks
|
||||
```
|
||||
@@ -6,7 +6,7 @@ compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.0.2"
|
||||
generatedBy: "1.1.1"
|
||||
---
|
||||
|
||||
Start a new change using the experimental artifact-driven approach.
|
||||
|
||||
@@ -6,7 +6,7 @@ compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.0.2"
|
||||
generatedBy: "1.1.1"
|
||||
---
|
||||
|
||||
Guide the user through their first complete OpenSpec workflow cycle. This is a teaching experience—you'll do real work in their codebase while explaining each step.
|
||||
|
||||
@@ -6,7 +6,7 @@ compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.0.2"
|
||||
generatedBy: "1.1.1"
|
||||
---
|
||||
|
||||
Sync delta specs from a change to main specs.
|
||||
|
||||
@@ -6,7 +6,7 @@ compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.0.2"
|
||||
generatedBy: "1.1.1"
|
||||
---
|
||||
|
||||
Verify that an implementation matches the change artifacts (specs, tasks, design).
|
||||
|
||||
Reference in New Issue
Block a user