# 测试流程控制条件

你可以在场景用例中新增流程控制条件（如循环、等待、分组），以满足更复杂的场景用例和流程配置需求，最终通过自动化测试功能解决复杂场景的测试任务。


<Background>

![image.png](https://api.apifox.com/api/v1/projects/5097254/resources/481656/image-preview)
</Background>


## 分组

当测试流程中的多个步骤存在关联时，可以将它们归类到同一个分组中。通过对测试步骤进行分组，提升场景用例的可读性和操作性。

**示例：** 将查询宠物详情、修改宠物信息、再次查看详情等步骤归类成分组。

**操作方法：**

1. 点击底部的按钮「添加步骤」，并选择「分组」。

2. 将要归类的步骤拖动到分组下，或直接在分组中添加新步骤。

    
    <Background>

    ![image.png](https://api.apifox.com/api/v1/projects/5097254/resources/481658/image-preview)
    </Background>


## ForEach 循环

在 ForEach 循环中，可以根据指定数组的元素个数，循环执行编排内的所有步骤，循环次数等于数组内的元素数量。支持提取当前循环的元素值和索引值。


在循环的附加设置中，支持添加“中断条件”和“遇到错误时”的循环异常处理选项，关于中断条件的操作可参考[判断规则](#判断规则)模块。


### ForEach 使用示例

假设有两个接口：**获取宠物信息列表**和**获取单个宠物详情**。需求是查询刚刚添加到宠物列表中的宠物详情。以下操作将展示如何通过 ForEach 循环编排这个场景。

1. **获取宠物列表**

    循环外的第一步是请求“获取宠物信息列表”接口，获取实际的列表数据。通常，这类接口会返回一个包含多个宠物基本信息（如宠物 ID、名称）的数组。

    
    <Background>

    ![image.png](https://api.apifox.com/api/v1/projects/5097254/resources/481659/image-preview)
    </Background>


2. **设置 ForEach 循环**
   
   在 ForEach 循环中，将数组来源设置为上一步响应结果中的宠物信息数组部分。示例中，可以通过 `data[*].id` 提取 `data` 中的所有 `id` 值作为数组。

    
    <Background>

    ![image.png](https://api.apifox.com/api/v1/projects/5097254/resources/481660/image-preview)
    </Background>


3. **循环内设置接口请求**
   
   在循环内添加“获取宠物详情”接口请求，并在请求参数 ID 中使用 ForEach 循环（element）中的元素值。

    
    <Background>

    ![image.png](https://api.apifox.com/api/v1/projects/5097254/resources/481661/image-preview)
    </Background>

4. **ForEach 循环执行示例**
   
   若从列表中返回了 3 个宠物信息（即数组内有 3 个元素），那么你可以在测试报告中看到 **“获取宠物信息列表”** 接口执行了 3 次，每次实际请求的宠物 ID 值是对应 **“获取单个宠物详情”** 真实返回的 3 个元素内的 ID 值。

    
    <Background>

    ![image.png](https://api.apifox.com/api/v1/projects/5097254/resources/481662/image-preview)
    </Background>


    运行 **“测试报告”** 后，可以在详情内看到实际请求的值和响应数据相一致。


    <Background>

    ![image.png](https://api.apifox.com/api/v1/projects/5097254/resources/481663/image-preview)
    </Background>


### 当前循环 element

系统自动将 ForEach 循环中设置的数组中的元素提取出来保存在此处指定的变量中。例如：`{{$.1.element.data.name}}`。每次循环开始时，会基于当前循环的数组素更新这个变量的实际值。如果元素是个对象，可以通过使用 JSONPath 来提取对象中的某个子级字段。


可以使用[动态值](https://docs.apifox.com/dynamic-values.md)功能可视化插入循环元素。


<Background>
 
![image.png](https://api.apifox.com/api/v1/projects/5097254/resources/481664/image-preview)
</Background>


除了可以通过动态值交互框进行可视化提取外，同样支持使用[变量表达式](https://docs.apifox.com/pass-data-between-test-steps.md)。


如果你需要在自定义脚本中获取此变量后进一步处理，可以使用例如`pm.variables.get("$.1.element")`来获取当前循环元素的实际值。


在自定义脚本界面右边有提供代码片段可以快速插入获取变量脚本。


<Background>

![image.png](https://api.apifox.com/api/v1/projects/5097254/resources/481665/image-preview)
</Background>


### 当前循环 index

当前循环的索引值，会保存在此变量中。例如：`{{$.1.index}}`。第一次循环开始时，这个索引值为 0，之后每次循环开始时会 `＋1` 更新值。


可以通过[动态值](https://docs.apifox.com/dynamic-values.md)交互框进行可视化提取，也同样支持使用[变量表达式](https://docs.apifox.com/pass-data-between-test-steps.md)。


如果你需要在自定义脚本中获取此变量后进一步处理，可以使用例如`pm.variables.get("$.1.index")`来获取当前循环元素的实际值。


## For 循环 

当测试步骤需要重复执行时，可以指定相应的循环次数重复执行。在循环的附加设置中，支持添加“中断条件”和“遇到错误时”的循环异常处理选项，关于中断条件的操作可参考[判断规则](#判断规则)模块。

### 使用示例

宠物店 IT 管理员登录宠物库存管理后台，将今日出售的 10 个宠物的在售状态自动批量变更为已售出。

1. 点击底部的按钮“添加步骤”，并选择“循环”。

2. 在循环次数中输入 10 次。

   
    <Background>

    ![image.png](https://api.apifox.com/api/v1/projects/5097254/resources/481666/image-preview)
    </Background>


3. 将和宠物信息有关的接口拖入该条件下内框中，或直接在该条件下直接添加测试步骤。

   
    <Background>

    ![image.png](https://api.apifox.com/api/v1/projects/5097254/resources/481667/image-preview)
    </Background>



### 当前循环 index

当前循环的索引值，会保存在此变量中。例如：`{{$.1.index}}`。第一次循环开始时，这个索引值为 0，之后每次循环开始时会 `＋1` 更新值。


可以通过[动态值](https://docs.apifox.com/dynamic-values.md)功能可视化插入循环元素。


<Background>

![image.png](https://api.apifox.com/api/v1/projects/5097254/resources/481668/image-preview)
</Background>


如果你需要在自定义脚本中获取此变量后进一步处理，可以使用例如`pm.variables.get("$.1.index")`来获取当前循环元素的实际值。



在自定义脚本界面右边有提供代码片段可以快速插入获取变量脚本。


<Background>

![image.png](https://api.apifox.com/api/v1/projects/5097254/resources/481669/image-preview)
</Background>



## 条件分支

在测试流程中，如果存在多条件判断，可以通过添加条件分支（If 语法）来区分执行的步骤。当满足设定条件时，该条件下的子步骤会执行；否则，子步骤将被跳过。具体可参考[判断规则](#判断规则)模块。


**示例：** 宠物店店主根据昨日的宠物出售情况进行判断。如果宠物已售出，则将其出售状态设置为“已售出”；否则，查询在售中的宠物列表。

1. 点击测试步骤上的`+`按钮，并选择「条件分支」。

2. 在 If 条件后的输入框中填写请求接口得到的变量 `saleStatus`，选择条件为“等于”，并输入比较值 true（即判断销售状态是否为 true，若是，则更改宠物信息为“已售出”）。

3. 将鼠标悬浮在条件分支操作栏上，点击出现的`＋ Else`按钮，新增“出售中的宠物列表”步骤（否则，查询在售中的宠物列表）。

4. 将测试步骤拖入相应的条件分支中。

    
<Background>

![image.png](https://api.apifox.com/api/v1/projects/5097254/resources/481670/image-preview)
</Background>



## 判断规则

在使用 `If` 条件分支时，可以设置判断规则，满足规则后执行子步骤，否则跳过。在 `ForEach` 和 `For` 循环的“中断条件”中，同样可以设置判断规则，以便灵活控制流程的执行。


| **规则**       | **说明**                               |
| -------------- | -------------------------------------- |
| **等于**       | 判断两个值是否相等。                   |
| **不等于**     | 判断两个值是否不相等。                 |
| **存在**       | 判断字段或变量是否存在。               |
| **不存在**     | 判断字段或变量是否不存在。             |
| **小于**       | 判断一个值是否小于另一个值。           |
| **小于或等于** | 判断一个值是否小于或等于另一个值。     |
| **大于**       | 判断一个值是否大于另一个值。           |
| **大于或等于** | 判断一个值是否大于或等于另一个值。     |
| **正则匹配**   | 判断字符串是否符合指定的正则表达式。   |
| **包含**       | 判断字符串或数组中是否包含指定的值。   |
| **不包含**     | 判断字符串或数组中是否不包含指定的值。 |
| **为空**       | 判断字段、数组或变量是否为空。         |
| **不为空**     | 判断字段、数组或变量是否不为空。       |
| **属于集合**   | 判断某个值是否属于指定的集合。         |
| **不属于集合** | 判断某个值是否不属于指定的集合。       |

在进行条件判断（例如大于、大于等于、等于或不等于）时，如果比较的值是整型或字符串，系统会**自动将字符串转换为数字**，以便进行准确的比较。例如，如果你比较一个字符串 `"18"` 与数字 `18`，系统会将字符串 `"18"` 转换为数字 `18`，然后进行比较。

<br />

### 等于

判断两个值是否相等。如果相等，条件成立，执行子步骤；否则，跳过。

- **示例**：  
  - **场景**：判断“前置步骤”获取的用户信息中的年龄是否等于 18 岁。 
  - **条件设置示例**：`{{$.5.resopnse.body.data.age}} 等于 18`
  - **实际执行效果**：如果从“前置步骤”的响应中提取的年龄字段值为 18（字符串会自动转换为数字），则执行后续子步骤，如显示特定信息或执行特定操作。

<br />

### 不等于

判断两个值是否不相等。如果不相等，条件成立，执行子步骤；否则，跳过。

- **示例**：  
  - **场景**：判断“前置步骤”获取的订单状态是否不等于“已支付”。  
  - **条件设置示例**：`{{$.4.response.body.data.status}} 不等于 已支付`  
  - **实际执行效果**：如果“前置步骤”提取到的订单状态为其他值（如“待支付”或“已发货”），则执行子步骤。

<br />

### 存在

判断某个字段或变量是否存在。如果存在，条件成立，执行子步骤；否则，跳过。

- **示例**：  
  - **场景**：检查“前置步骤”获取的用户数据中是否存在`email`字段。  
  - **条件设置示例**：`{{$.3.response.body.data.email}} 存在`  
  - **实际执行效果**：如果“前置步骤”提取的用户数据中有`email`字段，则执行子步骤。

<br />

### 不存在

判断某个字段或变量是否不存在。如果不存在，条件成立，执行子步骤；否则，跳过。

- **示例**：  
  - **场景**：检查“前置步骤”获取的用户数据中是否没有`phone`字段。  
  - **条件设置示例**：`{{$.2.response.body.data.phone}} 不存在`  
  - **实际执行效果**：如果“前置步骤”提取到的用户数据中没有`phone`字段，则执行子步骤。

<br />

### 小于

判断某个值是否小于另一个值。如果小于，条件成立，执行子步骤；否则，跳过。

- **示例**：  
  - **场景**：判断“前置步骤”获取的商品库存是否小于 10。  
  - **条件设置示例**：`{{$.1.response.body.data.stock}} 小于 10`  
  - **实际执行效果**：“前置步骤”提取到的库存值为 8，判定通过，执行子步骤。

<br />

### 小于或等于

判断某个值是否小于或等于另一个值。如果是，条件成立，执行子步骤；否则，跳过。

- **示例**：  
  - **场景**：判断“前置步骤”获取的用户年龄是否小于或等于 12 岁。  
  - **条件设置示例**：`{{$.2.response.body.data.age}} 小于或等于 12`  
  - **实际执行效果**：“前置步骤”提取的年龄值为 10，判定通过，执行子步骤。

<br />

### 大于

判断某个值是否大于另一个值。如果大于，条件成立，执行子步骤；否则，跳过。

- **示例**：  
  - **场景**：判断“前置步骤”获取的订单金额是否大于 1000 元。  
  - **条件设置示例**：`{{$.1.response.body.data.amount}} 大于 1000`  
  - **实际执行效果**：“前置步骤”提取的订单金额值为 1105 元，判定通过，执行子步骤。

<br />

### 大于或等于

判断某个值是否大于或等于另一个值。如果是，条件成立，执行子步骤；否则，跳过。

- **示例**：  
  - **场景**：判断“前置步骤”获取的用户积分是否大于或等于 500 分。  
  - **条件设置示例**：`{{$.3.response.body.data.points}} 大于或等于 500`  
  - **实际执行效果**：“前置步骤”提取的积分值为 600，判定通过，执行子步骤。

<br />

### 正则匹配

判断字符串是否匹配指定的正则表达式。如果匹配，条件成立，执行子步骤；否则，跳过。

- **示例**：  
  - **场景**：判断“前置步骤”获取的邮箱格式是否正确。  
  - **条件设置示例**：`{{$.2.response.body.data.email}} 正则匹配 /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/i`  
  - **实际执行效果**：如果“前置步骤”提取的邮箱格式符合正则表达式（例如`test@gmail.com`），则执行子步骤。

:::tip[]
正则表达式应使用“字面量”表示，字面量的正则表达式是由 `/` 包围的正则模式，并且可以在末尾添加修饰符（如 `g` 表示全局匹配，`i` 表示不区分大小写等），不清楚如何写正则表达式的可以学一下这里的 [MDN 文档](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_expressions)。
:::

<br />

### 包含

判断字符串或数组中是否包含指定值。如果包含，条件成立，执行子步骤；否则，跳过。

- **示例**：  
  - **场景**：判断“前置步骤”获取的用户权限列表是否包含“admin”权限。  
  - **条件设置示例**：`{{$.3.response.body.data.roles}} 包含 admin`  
  - **实际执行效果**：如果“前置步骤”提取的权限列表中包含“admin”，则执行子步骤。

<br />

### 不包含

判断字符串或数组中是否不包含指定值。如果不包含，条件成立，执行子步骤；否则，跳过。

- **示例**：  
  - **场景**：判断“前置步骤”获取的用户购物车中是否没有某个商品。  
  - **条件设置示例**：`{{$.4.response.body.data.cartItems}} 不包含 productId123`  
  - **实际执行效果**：如果“前置步骤”提取的购物车数组中不包含“productId123”商品，则执行子步骤。

<br />

### 为空

判断字段、数组或变量是否为空。如果为空，条件成立，执行子步骤；否则，跳过。

- **示例**：  
  - **场景**：检查“前置步骤”获取的用户备注字段是否为空。  
  - **条件设置示例**：`{{$.2.response.body.data.remarks}} 为空`  
  - **实际执行效果**：如果“前置步骤”提取的备注字段为空，则执行子步骤。

<br />

### 不为空

判断字段、数组或变量是否不为空。如果不为空，条件成立，执行子步骤；否则，跳过。

- **示例**：  
  - **场景**：检查“前置步骤”获取的订单备注是否填写。  
  - **条件设置示例**：`{{$.1.response.body.data.orderRemarks}} 不为空`  
  - **实际执行效果**：如果“前置步骤”提取的订单备注不为空，则执行子步骤。

<br />

### 属于集合

判断某个值是否属于一个集合。如果属于，条件成立，执行子步骤；否则，跳过。

- **示例**：  
  - **场景**：判断“前置步骤”获取的用户选择的产品是否在推荐产品列表中。  
  - **条件设置示例**：`{{$.3.response.body.data.productId}} 属于集合 ["prod123", "prod456", "prod789"]`  
  - **实际执行效果**：“前置步骤”提取的产品 ID 为“prod456”，条件成立，执行子步骤。

:::tip[]
在 Apifox 中，输入集合时，每个元素需单独输入，并通过按下回车键进行分隔。
:::

<br />

### 不属于集合

判断某个值是否不属于一个集合。如果不属于，条件成立，执行子步骤；否则，跳过。

- **示例**：  
  - **场景**：判断“前置步骤”获取的用户输入的优惠码是否未在已使用优惠码列表中。  
  - **条件设置示例**：`{{$.4.response.body.data.promoCode}} 不属于集合 ["usedCode1", "usedCode2"]`  
  - **实际执行效果**：如果“前置步骤”提取的优惠码为“newPromo”，条件成立，执行子步骤。


## 等待时间

当测试流程中某个步骤需要执行后需要等待一段时间时，比如 A 步骤需要等待若干时间后再执行 B 步骤，可以通过新增等待条件来解决。

<br />

**示例：** 模拟用户查看宠物详情，浏览 `1000ms` 后，将该宠物详情信息中的浏览状态进行更新。

1. 点击底部的按钮「添加步骤」，并选择「等待时间」。

2. 输入需要等待的时间 1000（单位毫秒）。

    
<Background>

![image.png](https://api.apifox.com/api/v1/projects/5097254/resources/481671/image-preview)
</Background>



