### 1、关联接口数据保存

```text
# test_api.json
[# 用例列表，每一个元素都是一条用例
    {
        "caseLevel": "P1",   # 用例等级
        "caseName": "购物车下单", # 用例名称
        "caseTier": "项目-首页-添加购物车-下单", # 用例目录
        "parametrize": [ # 参数化数据，每一行会参数化出一条测试用例
            {"_title": "参数化数据1", "username": "A02441", "password":  "Xu173798"},
            {"_title": "参数化数据2", "username": "A02441", "password":  "xu1"}
        ],
        "caseNodes": [ # 测试用例节点, 每一个元素为一个接口测试步骤
            {
                "step_name": "登录飞熊平台", # 测试步骤名
                "driver": "api", # 使用的驱动方式
                "user": "user2", # 使用的用户 api对应HttpUser的user2
                "param": { # requests.request对应的参数
                    "headers": {}, # 请求头
                    "method": "post", # 请求方法
                    "url": "http://124.71.56.59:8070/api/auth/login/", # 请求域名不填写默认使用projectSetting.json default域名 eg. /api/auth/login/= {default}/api/auth/login/
                    "json": {"username": "@{username}", "password":  "@{password}"} # @{xxxx}为参数替换，后续会详述
                },
                "assert_content": [{"eq":["status_code", 200]}], # 用例断言
                "extract": [["token", "token"]] # 用例参数提取
            }
        ]
    }
]
```

提取逻辑存在于caseNodes.[*].extract

```text
"extract": [  # 每个列表元素会做一次变量保存操作

    [
        "token", # 变量保存表达式，支持多种表达式。后续讲解以该表达式为主
        "token2" # 表达式保存的变量起个别名，防止变量名重复后，之前保存变量被覆盖
	]，
	"token", # 若不想命名别名时，可直接写字符串。别名即为表达式本身（仅支持key表达式）
] 
# 列表每个
```



#### 1.1 关联接口返回数据为json

** 以下重点介绍均为保存表达式编写 **

##### 1.1.1 保存返回数据请求头结果

```txt
extact= [[expression, alias]]
expression = "header:content-type"
alias = "content-type"
```

-   header 表名需要取值字段为请求头字段

-   content-type 为请求头字段名称

-   使用 @{变量名} 在结构中可执行参数替换

    ```text
    [# 用例列表，每一个元素都是一条用例
        {
            "caseLevel": "P1",   # 用例等级
            "caseName": "购物车下单", # 用例名称
            "caseTier": "项目-首页-添加购物车-下单", # 用例目录
    
            "caseNodes": [ # 测试用例节点, 每一个元素为一个接口测试步骤
                {
                     "param":{"headers": {"content-type": "@{content-type}"}}   
      
                }
            ]
        }
    ]
    ```

##### 1.1.2 使用key语法保存json返回数据

```txt
extact= [[expression, alias], "expression"]
[expression, alias]: expression为表达式， alias为保存数据后变量别名
"expression": 此写法时 expression=alias，仅适用于key方法, 该key要存在于返回数据json结构中，否则存储的结果为空列表
```



example：接口返回数据

```json
{
    "code": 200,
    "data": {
        "name": "李天一",
        "age": 20,
        "sex": "men"
    },
    "productList": [
        {"id": 1, "productName": "小鱼干"},
        {"id": 2, "productName": "小面包"},
        {"id": 3, "productName": "小浆果"},
        {"id": 4, "productName": "大列巴"}
    ]
}
```

###### ① 保存key为name的数据(此方法无视json层级结构，取到name值全部保存到变量列表中)

```txt
extact= [[expression, alias]]
expression = "name"
alias = "username"
```

```txt
@{username} 即可替换为 "李天一"
```

###### ② 保存key=productName的值

```txt
extact= ["expression"]
expression = "productName"
```

使用：@{变量名.索引}取到对应的值， 不填写索引时默认索引为 0

```txt
@{productName} : 可将结果替换为： 小鱼干
@{productName.1}：可将结果替换为：小面包
@{productName.-1}: 可将结果替换为：大列巴
@{productName.all}: 可将结果替换为列表： ["小鱼干", "小面包", "小浆果", "大列巴"]
```



##### 1.1.3 使用jmespath语法保存json返回数据

[jmespath语法传送门](https://blog.csdn.net/qq_33528044/article/details/115454953)

```json
{
    "code": 200,
    "data": {
        "name": "李天一",
        "age": 20,
        "sex": "men"
    },
    "productList": [
        {"id": 1, "productName": "小鱼干"},
        {"id": 2, "productName": "小面包"},
        {"id": 3, "productName": "小浆果"},
        {"id": 4, "productName": "大列巴"}
    ]
}
```

① 保存商品名为小鱼干的商品id (条件取值)

```txt
extact= [[expression, alias]]
expression = "productList[?productName=='小鱼干'].id"
alias = "productId"
```

使用：

```txt
@{productId}: 即可替换为小鱼干id 1
```

② 保存商品id大于2的所有商品 (注意2的符号)

```txt
extact= [[expression, alias]]
expression = "productList[?id>`2`].productName"
alias = "productName"
```

使用：

```txt
@{productName}: 即可替换为id大于2的商品名 小浆果
```

#### 1.2 接口返回数据为html数据时

[parsel语法传送门](https://blog.csdn.net/qq_33528044/article/details/119424026)

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>标签选择器</title>
</head>
<body>
    <p>css标签选择器的介绍</p>
    <p class='p'>标签选择器、类选择器、ID选择器</p>
    <a href="https://www.baidu.com" title="百度搜索">百度一下</a>
    <span> 我是一个span标签</span>
    <div id="map">
        <p class="content">早上祝您端午节快乐！</p>
    </div>
    <span id="map">
        <p class="content">晚上祝您端午节快乐！</p>
    </span>
    <span id="text">你好，我是一行文字</span>
</body>
</html>
```

① xpath 提取百度一下文本

```txt
extact= [[expression, alias]]
expression = "xpath://a[@title='百度搜索']/text()" (xpath:(parsel第三方表达式))
alias = "title"
```

使用：（同key方法，以下不在做赘述）

② xpath 提取百度一下连接属性

```txt
extact= [[expression, alias]]
expression = "xpath://a[@title='百度搜索']/@href"  ()
alias = "title"
```

③ 使用css提取文本

```txt
extact= [[expression, alias]]
expression = "css:#map .content::text"  # (提取文本['早上祝您端午节快乐！', '晚上祝您端午节快乐！'])
alias = "text"
```


④ 使用css提取标签属性

```txt
extact= [[expression, alias]]
expression = "css:a[title='百度搜索']::attr(href)"   # (提取文本['早上祝您端午节快乐！', '晚上祝您端午节快乐！'])
alias = "href"
```


⑤ 使用正则提取html内容

```txt
extact= [[expression, alias]]
expression = 'xpath:<a href="(.*?)"'  # (提取文本['https://www.baidu.com'])
alias = "href"
```



### 2. 可使用变量替换语法位置

"# √" 标记字段均可使用 @{变量名.索引} 进行数据替换，在第3节中介绍的函数替换也可使用

```text
[
    {
        "caseLevel": "P1",
        "caseName": "购物车下单",
        "caseTier": "项目-首页-添加购物车-下单",
        "parametrize": [
            {"_title": "参数化数据1", "username": "A02441", "password":  "Xu173798"},
            {"_title": "参数化数据2", "username": "A02441", "password":  "xu1"}
        ],
        "caseNodes": [
            {
                "step_name": "登录飞熊平台",
                "driver": "api",
                "user": "user2",
                "param": {}, # √
                "assert_content": [{"eq":["status_code", 200]}], # √
                "extract": [["token", "token"]]  # √
            }
        ]
    }
]
```



