场景型接口自动化skills

4 阅读6分钟
# 场景型接口自动化编写指南

当多个API属于同一业务功能场景(如白名单管理的增删改查),需要按场景整体规划测试,而非逐个API独立编写。本文档总结编写场景型接口自动化的经验和注意事项。

---

## 什么是场景型接口

场景型接口是指一组在业务上紧密关联、形成完整CRUD闭环的API集合。典型特征:

| 特征 | 示例 |
|------|------|
| 同一资源的增删改查 | AddWhiteListGroup / DeleteWhiteListGroup / ModifyWhiteListGroup / DescribeWhiteListGroup |
| 操作之间有前置依赖 | 必须先Add才能Modify/Delete |
| 共享同一组参数规则 | 分组名命名规则、IP格式要求对多个API通用 |
| 需要跨API验证 | Add之后用Describe验证,Delete之后用Describe确认删除 |

**已完成的场景型接口案例**:
- 白名单管理:ModifyIpWhiteList + AddWhiteListGroup + DeleteWhiteListGroup + ModifyWhiteListGroup + DescribeWhiteListGroup(5个API,41个测试用例)

---

## 编写流程(与单API的区别)

### 第一步:整体规划(最重要的区别)

单API编写时只需关注自身参数,场景型要先理清API间的依赖关系和执行顺序:

```
1. 梳理API关系图
   DescribeWhiteListGroup (查询) ← 所有写操作的验证手段
   AddWhiteListGroup (创建)     → ModifyWhiteListGroup (修改) → DeleteWhiteListGroup (删除)
   ModifyIpWhiteList (修改全局) ← 独立,但共享IP格式规则

2. 确定编写顺序
   先写查询(Describe) → 再写创建(Add) → 再写修改(Modify) → 最后写删除(Delete)
   原因:查询API是其他API的验证手段,必须先确保它能工作

3. 识别共享约束
   - 分组名规则:1-64字符,只允许中文/英文/数字
   - IP格式:CIDR表示法,逗号分隔
   - 这些约束在多个API的异常值测试中共享
```

### 第二步:统一检查Service层

场景型接口一次性涉及多个service方法,必须批量检查SDK参数与service封装是否一致:

```python
# 错误做法:假设service方法已经正确,直接写测试
# 正确做法:先读每个SDK Parameters类的源码,确认:
#   1. 构造函数有哪些必填参数
#   2. 有哪些setter方法(可选参数)
#   3. service方法的调用是否与SDK匹配

# 白名单场景发现的问题:
# AddWhiteListGroupParameters(regionId, cacheInstanceId, name)  ← SDK用name
# service层错误地调用了 setGroupName(), setIpList(), setGroupType()  ← 全部不存在
```

### 第三步:编写测试(场景型特有模式)

#### 模式1:写操作的P0必须包含"创建→验证→清理"完整闭环

```python
def test_add_white_list_group_basic(self, setup_clients, instance_data):
    """P0用例必须是完整闭环,不能只调一下API就算通过"""
    # 1. 等待实例就绪
    service.wait_for_instance_status(cacheInstanceId)

    # 2. 执行写操作
    success, result = service.add_white_list_group(cacheInstanceId, group_name, ips=test_ips)
    assert success, f"添加失败: {result}"

    # 3. 用查询API交叉验证(不能只信任写操作的返回值)
    query_success, query_result = service.describe_white_list_group(cacheInstanceId)
    assert query_success
    group_names = [g.get('name') for g in query_result['whiteLists']]
    assert group_name in group_names, f"新分组未在列表中"

    # 4. 清理测试数据(防止影响其他测试)
    service.delete_white_list_group(cacheInstanceId, group_name)
```

#### 模式2:Delete的P0必须"先创建再删除再验证"

```python
def test_delete_white_list_group_basic(self, setup_clients, instance_data):
    """Delete测试不能依赖已存在的数据,必须自己创建"""
    # 1. 先创建一个用于删除的分组
    service.add_white_list_group(cacheInstanceId, test_group_name, ips="10.0.0.1/32")

    # 2. 执行删除
    success, result = service.delete_white_list_group(cacheInstanceId, test_group_name)
    assert success

    # 3. 查询验证已删除
    query_result = service.describe_white_list_group(cacheInstanceId)
    group_names = [g.get('name') for g in query_result['whiteLists']]
    assert test_group_name not in group_names, "删除后分组仍存在"
```

#### 模式3:Modify的P0必须"创建→修改→验证→清理"

```python
def test_modify_white_list_group_basic(self, setup_clients, instance_data):
    """Modify测试需要自建数据,修改后验证,最后清理"""
    # 1. 创建测试分组
    service.add_white_list_group(cacheInstanceId, test_group_name, ips=original_ips)

    # 2. 修改
    success, result = service.modify_white_list_group(cacheInstanceId, test_group_name, new_ips)
    assert success

    # 3. 查询验证修改生效
    query_result = service.describe_white_list_group(cacheInstanceId)
    target = [g for g in query_result['whiteLists'] if g['name'] == test_group_name]
    assert new_ips in target[0]['ips']

    # 4. 清理
    service.delete_white_list_group(cacheInstanceId, test_group_name)
```

#### 模式4:修改全局状态的API必须"保存→修改→验证→恢复"

```python
def test_modify_ip_white_list_basic(self, setup_clients, instance_data):
    """修改全局配置(如IP白名单)必须先保存原始值,测试完恢复"""
    # 1. 保存原始状态
    _, original_result = service.describe_ip_white_list(cacheInstanceId)
    original_ips = original_result.get('ipWhiteList', [])

    # 2. 修改
    success, result = service.modify_ip_white_list(cacheInstanceId, test_ips)
    assert success

    # 3. 验证
    _, verify_result = service.describe_ip_white_list(cacheInstanceId)
    # ... 验证逻辑

    # 4. 恢复原始状态(关键!)
    if original_ips:
        service.modify_ip_white_list(cacheInstanceId, original_ips)
```

---

## 场景型测试的关键注意点

### 1. 测试数据命名:必须符合API参数规则

**踩坑经验**:白名单分组名只允许中文、英文字母和数字,使用 `autotest_add_12345`(含下划线)被API拒绝。

```python
# 错误:含下划线/连字符
test_group_name = f"autotest_add_{int(time.time()) % 100000}"

# 正确:纯字母+数字
test_group_name = f"autotestadd{int(time.time()) % 100000}"
```

**通用规则**:生成测试数据名称前,先确认目标API的参数校验规则。

### 2. 测试数据隔离:用时间戳确保唯一性

```python
import time
# 每次运行生成唯一名称,避免与其他测试或手动数据冲突
test_group_name = f"autotestmod{int(time.time()) % 100000}"
```

### 3. 每个写操作之间必须等待实例状态

```python
# 添加分组后
service.wait_for_instance_status(cacheInstanceId)
# 才能修改分组
service.modify_white_list_group(...)
service.wait_for_instance_status(cacheInstanceId)
# 才能删除分组
service.delete_white_list_group(...)
```

### 4. 清理逻辑要容错,不能因清理失败导致测试报错

```python
# 清理失败只告警,不影响测试结果
del_success, del_result = service.delete_white_list_group(cacheInstanceId, test_group_name)
if not del_success:
    logging.warning(f"清理分组失败: {del_result}")
```

### 5. 场景型P1测试应测试跨API的业务约束

除了单API的错误处理外,场景型测试的P1应覆盖:

| 测试场景 | 覆盖API | 预期 |
|---------|---------|------|
| 重复创建同名分组 | Add + Add | 第二次返回409冲突 |
| 删除不存在的分组 | Delete | 返回错误 |
| 修改不存在的分组 | Modify | 返回错误 |
| 删除系统默认分组 | Delete(default) | 返回错误,不允许删除 |
| 修改系统默认分组IP | Modify(default) | 允许修改(但需恢复) |

### 6. 异常值测试可跨API共享参数化数据

同一场景下的多个API通常共享相同的参数规则,异常值测试的参数化数据可以复用:

```python
# 分组名异常值 — Add/Delete/Modify 三个API共用
INVALID_GROUP_NAMES = [
    ("", "空字符串"),
    ("a" * 256, "超长名称"),
    ("特殊字符@#$%", "特殊字符"),
]

# IP格式异常值 — ModifyIpWhiteList/ModifyWhiteListGroup 共用
INVALID_IPS = [
    ("invalid-ip", "格式错误"),
    ("999.999.999.999/32", "无效IP段"),
    ("192.168.1.1/33", "无效CIDR掩码"),
]
```

### 7. Service层批量修复时的检查清单

场景型接口一次涉及多个service方法,修复前必须逐一对照SDK:

```
对每个API执行以下检查:
□ 读SDK的 {ApiName}Parameters 类源码
□ 确认构造函数的必填参数列表和顺序
□ 确认有哪些 setXxx() 方法(可选参数)
□ 确认service方法的参数是否与SDK对齐
□ 确认参数类型(字符串/列表/字典)
```

---

## 场景型接口的用例数量参考

| API类型 | P0 | P1 | P2 | 合计 |
|---------|----|----|-----|------|
| Describe(查询) | 1 | 3(错误处理、字段结构、默认值) | 2(边界、异常ID) | 6 |
| Add(创建) | 1(创建+验证+清理) | 3(错误处理、不带可选参数、重复创建) | 1(异常名称) | 5+ |
| Delete(删除) | 1(创建+删除+验证) | 3(错误处理、不存在、删default) | 1(异常名称) | 5+ |
| Modify(修改) | 1(创建+修改+验证+清理) | 3(错误处理、不存在、修改default) | 1(异常IP) | 5+ |
| ModifyGlobal(全局修改) | 1(保存+修改+验证+恢复) | 3(错误处理、多值、特殊值) | 1(异常格式) | 5+ |

白名单场景总计:5个API → 41个测试用例(含参数化展开)

---

## 相关文档

- [踩坑经验 - lessons_learned.md](lessons_learned.md)
- [排查指南 - error_debugging_guide.md](error_debugging_guide.md)
- [完整示例 - complete_example.md](complete_example.md)
- [Skill 主文档 - ../SKILL.md](../SKILL.md)