YAML 用例语法格式注意事项¶
APIMeter测试用例格式是基于YAML,YAML本身有自己语法要求,加上测试用例中包含自定义变量、全局变量转义、自定义函数、列表参数、对象参数、复杂嵌套对象参数、链式参数、正则参数等内容,写测试用例有时会遇到用例文件解析失败的语法问题,因此梳理一下常见的错误写法格式,汇总为一份YAML用例语法格式注意事项文档,方便大家避坑和排查问题。
一、YAML语法排查¶
1、IDE内置YAML语法高亮¶
如果用例存在语法高亮异常不规则,则说明存在YAML语法问题,需要纠正
2、内置命令,一键排查¶
# Validate YAML/JSON api/testcase/testsuite format.
apimeter --validate [VALIDATE ...]
# Example: testcase file
apimeter --validate api/youcloud/account/query_areaCodeList_api.yml
# Example: testcase folder
apimeter --validate api/youcloud/account
二、常见错误概览¶
错误信息 | 可能原因 | 解决方案 |
---|---|---|
mapping values are not allowed here |
函数参数中的{} 被误认为字典 |
用引号包围整个函数调用 |
while parsing a flow sequence |
列表语法中特殊字符未处理 | 使用多行格式或加引号 |
found undefined tag handle |
变量引用格式错误 | 检查${} 格式和变量名 |
VariableNotFound、FunctionNotFound |
变量、函数未定义或引用错误 | 检查变量、函数定义和引用语法 |
三、常见问题示例¶
1、校验器错误写法 ❌¶
validate:
- eq: [${validate_token($token)}, true] # YAML的列表(数组)包含特殊字符的函数调用时,会导致语法错误
报错信息¶
ERROR while parsing a flow sequence
expected ',' or ']', but got '{'
✅ 正确写法1:单行格式(加引号)¶
validate:
- eq: ["${validate_token($token)}", true]
✅ 正确写法2:多行格式(推荐)¶
validate:
- eq:
- ${validate_token($token)}
- true
2、字典参数错误写法 ❌¶
sign: ${get_sign_v3({device_sn: $device_sn, os_platform: $os_platform})} # 函数参数包含花括号 `{}` 时,YAML解析器会将其误认为是字典定义,导致语法错误
报错信息¶
ERROR mapping values are not allowed here
✅ 正确写法1:使用双引号和转义¶
sign: "${get_sign_v3({\"device_sn\": $device_sn, \"os_platform\": $os_platform})}"
✅ 正确写法2:使用双引号+单引号¶
sign: "${get_sign_v3({'device_sn': $device_sn, 'os_platform': $os_platform})}"
✅ 正确写法3:使用双引号¶
sign: "${get_sign_v3({device_sn: $device_sn, os_platform: $os_platform})}" # YAML原生字典语法(推荐)
四、YAML用例正确语法姿势¶
1、各种函数调用的正确写法¶
teststeps:
-
name: test functions
validate:
# 简单函数调用 - 无需引号
- eq: [${get_timestamp()}, 1234567890]
# 复杂函数调用 - 需要引号
- eq: ["${validate_data({\"key\": $value})}", true]
# 列表参数 - 需要引号
- eq: ["${process_list([$item1, $item2])}", "success"]
# 多行格式 - 总是安全
- eq:
- ${complex_function($param1, $param2)}
- expected_result
2、包含列表参数的函数调用的正确写法¶
sign: ${get_sign($device_sn, $os_platform, $app_version)}
sign: "${get_sign_v2([$device_sn, $os_platform, $app_version])}"
sign: ${get_sign_v2([$device_sn, $os_platform, $app_version])}
3、全局变量正确用法¶
APIMeter提供以下内置全局变量,无需使用$
前缀¶
# 内置全局变量列表
- content / body / text / json # 响应体数据
- status_code # HTTP状态码
- headers # 响应头
- cookies # Cookie信息
- elapsed # 请求耗时
- encoding / ok / reason / url # 其他响应信息
✅ 正确使用方式¶
# 直接使用全局变量
validate:
- eq: [status_code, 200]
- eq: [content.token, $expected_token]
- eq: [headers.content-type, "application/json"]
# 在函数中引用全局变量
script:
- ${validate_response(content)}
- ${check_headers(headers)}
❌ 错误写法¶
# 全局变量前加$符号
validate:
- eq: [$status_code, 200] # 错误!
- eq: [$content.token, "abc"] # 错误!
当数据字段与全局变量同名时,使用反斜杠\
转义¶
✅ 正确使用转义¶
# 当响应数据中有名为"content"的字段时
validate:
- eq:
- ${check_data_not_null(content.data.lines, \content)} # \content 表示字符串 "content"
- True
# 支持转义所有全局变量
script:
- ${validate_field_name(\status_code, \headers, \content)}
❌ 错误写法¶
# 直接使用会被解析为全局变量而非字面量
validate:
- eq:
- ${check_field_name(content.data, content)} # content被解析为全局变量值
- True
4、引号正确使用规则¶
何时必须使用引号¶
- 包含花括号
{}
的函数调用 - 包含方括号
[]
的函数调用 - 单行列表格式中的复杂表达式
- 包含冒号
:
的字符串值
✅ 正确示例¶
# 字典参数必须加引号
sign: "${get_sign({device: $device_sn})}"
# 包含冒号的值必须加引号
url: "http://example.com:8080/api"
# 单行校验器必须加引号
validate:
- eq: ["${complex_func($param)}", "expected"]
# 列表参数建议加引号
data: "${process_list([$item1, $item2])}"
❌ 错误示例¶
# 字典参数无引号 - 解析错误
sign: ${get_sign({device: $device_sn})}
# 单行校验器无引号 - 解析错误
validate:
- eq: [${func($param)}, "expected"]
五、最佳实践¶
- 检验器优先使用多行格式:更清晰,不容易出错
- 复杂参数统一加引号:包含
{}[]
等特殊字符时 - 保持团队脚本风格一致:在同一个项目中使用统一的风格
- 及时测试验证语法:修改YAML后及时验证语法正确性
- 最后提醒:遇到语法错误时,首先检查引号使用和格式规范,90%的问题都能快速解决! 🎯
🔧 附录:测试用例模板¶
teststeps:
- name: "基础用例模板"
variables:
expected_status: 200
expected_field: "success"
request:
url: "/api/endpoint"
method: GET
headers:
Authorization: "Bearer ${get_auth_token($user_id)}"
validate:
- eq: [status_code, $expected_status]
- eq: [content.status, $expected_field]
- eq:
- "${validate_response_format(content)}"
- True
script:
- assert status_code == 200
- assert content.success is True
- ${log_response_time(elapsed.total_seconds)}