前言
我们在做自动化测试的时候,通常会把配置信息和测试数据存储到特定的文件中,以实现数据和脚本的分离,从而提高代码的易读性和可维护性,便于后期优化。
而配置文件的形式更是多种多样,比如:ini、yaml、json、toml、py、xml、properties 等。
YAML是一种轻量级的文本格式,可以用于存储结构化数据,非常适合用作测试数据。
在使用YAML数据驱动进行自动化测试时,通常需要准备一个或多个YAML文件,其中包含了测试用例的数据和预期结果。测试脚本会读取这些文件,并根据文件中的数据来执行对应的测试步骤,然后验证实际结果与预期结果是否一致。
下面我们就来详探接口自动化测试中的 YAML 数据驱动
1、什么是 YAML
YAML:YAML Ain’t a Markup Language,翻译过来就是YAML 不是一种标记语言。
它是一种以数据为中心的标记语言,比 XML 和 JSON 更适合作为配置文件。
YAML 的配置文件后缀为.yml 或.yaml,如:caituotuo.yml 或 caituotuo.yaml。
YAML 的语法和其他高级语言类似,并且可以简单表达清单、散列表,标量等数据形态。它使用空白符号缩进和大量依赖外观的特色,特别适合用来表达或编辑数据结构、各种配置文件、倾印调试内容、文件大纲等。
2、YAML 语法
1)基本语法
使用缩进表示层级关系;
缩进不允许使用 tab,只允许空格(官方说法不允许使用 tab,当然如果你使用 tab 在某些地方也是可以的,例如在 PyCharm 软件上);
缩进的空格数不重要,只要相同层级的元素左对齐即可;
大小写敏感;
前面加上 #表示注释;
-
req:
-
username: xxxxxx # 这是姓名
-
gender: Boy
-
ip: ******
-
blog: www.xxxxxx.com
-
res:
-
status: 1
-
code: 200
2)数据类型
对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
纯量(scalars):单个的、不可再分的值,又称字面量
纯量是指单个的,不可拆分的值,例如:数字、字符串、布尔值、Null、日期等,纯量直接写在键值对的 value 中即可。
字符串:
默认情况下字符串是不需要使用单引号或双引号的
username: 张三
- 1
当然使用双引号或者单引号包裹字符也是可以的
-
username: 'Hello world 张三'
-
username: "Hello world 张三"
字符串可以拆成多行,每一行会被转化成一个空格
-
# 字符串可以拆成多行,每一行会被转化成一个空格 '测试 张三'
-
username3: 测试
-
张三
布尔值:
-
boolean:
-
- TRUE #true,True都可以
-
- FALSE #false,False都可以
-
# {'boolean': [True, False]}
数字:
-
float:
-
- 3.14
-
- 6.8523015e+5 #可以使用科学计数法
-
int:
-
- 123
-
- 0b1010_0111_0100_1010_1110 #二进制表示
-
# {'float': [3.14, 685230.15], 'int': [123, 685230]}
Null:
-
null:
-
nodeName: 'node'
-
parent: ~ #使用~表示null
-
parent2: None #使用None表示null
-
parent3: null #使用null表示null
-
# {None: {'nodeName': 'node', 'parent': None, 'parent2': 'None', 'parent3': None}}
时间和日期:
-
date:
-
- 2023-04-18 #日期必须使用ISO 8601格式,即yyyy-MM-dd
-
datetime:
-
- 2023-04-18T15:09:30+08:00 #时间使用ISO 8601格式,时间和日期之间使用T连接,最后使用+代表时区
-
# {'date': [datetime.date(2023, 4, 18)], 'datetime': [datetime.datetime(2023, 4, 18, 15, 9, 30, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800)))]}
对象
使用 key:[空格]value 的形式表示一对键值对(空格不能省略),例如:blog: caituotuo.top。
行内写法:
key: {key1: value1, key2: value2, ...}
普通写法,使用缩进表示对象与属性的层级关系:
-
key:
-
child-key: value
-
child-key2: value2
数组
以 - 开头的行表示构成一个数组。
普通写法:
-
name:
-
- 测试
-
- 张三
-
- 三
YAML 支持多维数组,可以使用行内表示:
key: [value1, value2, ...]
数据结构的子成员是一个数组,则可以在该项下面缩进一个空格:
-
username:
-
-
-
- 测试张三
-
- 张三
-
- 张
-
-
-
- A
-
- B
-
- C
-
# {'username': [['测试张三', '张三', '张'], ['A', 'B', 'C']]}
相对复杂的例子:
companies 属性是一个数组,每一个数组元素又是由 id、name、price 三个属性构成
-
companies:
-
-
-
id: 1
-
name: zhangshan
-
price: 300W
-
-
-
id: 2
-
name: 测试张三
-
price: 500W
-
# {'companies': [{'id': 1, 'name': 'zhangshan', 'price': '300W'}, {'id': 2, 'name': '测试张三', 'price': '500W'}]}
数组也可以使用 flow 流式的方式表示:
companies2: [ { id: 1,name: zhangshan,price: 300W },{ id: 2,name: 测试张三,price: 500W } ]
复合结构
以上三种数据结构可以任意组合使用,以实现不同的用户需求,例如:
-
platform:
-
- A1
-
- A2
-
- A3
-
sites:
-
A1: 测试张三
-
A2: 测试李四
-
A3: xxxxxx.com
-
# {'platform': ['A1', 'A2', 'A3'], 'sites': {'A1': '测试张三', 'A2': '测试李四', 'A3': 'xxxxxx.com'}
3、引用
& 锚点和 * 别名,可以用来引用。
举个例子:
& 用来建立锚点 defaults,<< 表示合并到当前数据,* 用来引用锚点
-
defaults: &defaults
-
adapter: postgres
-
host: localhost
-
development:
-
database: myapp_development
-
<<: *defaults
-
test:
-
database: myapp_test
-
<<: *defaults
等价于:
-
defaults:
-
adapter: postgres
-
host: localhost
-
development:
-
database: myapp_development
-
adapter: postgres
-
host: localhost
-
test:
-
database: myapp_test
-
adapter: postgres
-
host: localhost
4、组织结构
一个 YAML 文件可以由一个或多个文档组成,文档之间使用 — 作为分隔符,且整个文档相互独立,互不干扰,如果 YAML 文件只包含一个文档,则 — 分隔符可以省略。
-
---
-
website:
-
name: 测试张三
-
url: xxxxxx.com
-
---
-
website: { name: 测试张三,url: www.xxxxxx.com }
-
---
-
A1: 测试张三
-
---
-
A2: 测试张三
-
f7 = "./files/多文档.yml"
-
with open(f7, "r", encoding="UTF-8") as f:
-
content = yaml.safe_load_all(f)
-
for i in content:
-
print(i)
5、实战
封装思路
将 YAML 相关操作封装成 CommonUtil 公共模块,之后直接引入调用即可。
相关功能:
读取 yaml 文件数据;
将 yaml 数据转换成 json 格式;
可以动态设置参数;
这里要说一下动态设置参数
在自动化测试中,肯定不能把所有的参数都写死,因此就会用到参数化,例如:提取前一个接口的返回值作为后一个接口的入参,这里通过 Python 中的 Template 模块进行动态参数的设置
yaml 文件中通过 $变量名的形式设置变量
username: $username
给变量附上具体的值
-
with open(yaml_path, "r", encoding="UTF-8") as f:
-
text = f.read()
-
# Template(text).safe_substitute(key_value)
-
Template(text).safe_substitute({"username": "测试张三"}) # username为变量名
完整代码
-
import os
-
from string import Template
-
import yaml
-
class YamlUtil:
-
@staticmethod
-
def yaml_util(yaml_path, key_value=None):
-
"""
-
读取yml文件 设置动态变量
-
:param yaml_path: 文件路径
-
:param key_value: 动态变量 如:{"username": "测试张三"} yaml中的变量:$username
-
:return:
-
"""
-
try:
-
with open(yaml_path, "r", encoding="UTF-8") as f:
-
text = f.read()
-
if key_value is not None:
-
re = Template(text).safe_substitute(key_value)
-
json_data = yaml.safe_load(re)
-
else:
-
json_data = yaml.safe_load(text)
-
return json_data
-
except FileNotFoundError:
-
raise FileNotFoundError("文件不存在")
-
except Exception:
-
raise Exception("未知异常")
-
@staticmethod
-
def multiple(yaml_path):
-
"""
-
多文档
-
:param yaml_path: yaml文件路径
-
:return: list
-
"""
-
json_data = []
-
try:
-
with open(yaml_path, "r", encoding="UTF-8") as f:
-
content = yaml.safe_load_all(f)
-
for i in content:
-
json_data.append(i)
-
return json_data
-
except FileNotFoundError:
-
raise FileNotFoundError("文件不存在")
-
except Exception:
-
raise Exception("未知异常")
-
if __name__ == '__main__':
-
f1 = "./files/初体验.yml"
-
print(YamlUtil().yaml_util(f1))
-
f2 = "./files/纯量.yml"
-
print(YamlUtil().yaml_util(f2))
-
f3 = "./files/数组.yml"
-
print(YamlUtil().yaml_util(f3))
-
f4 = "./files/复合结构.yml"
-
print(YamlUtil().yaml_util(f4))
-
f5 = "./files/引用.yml"
-
print(YamlUtil().yaml_util(f5))
-
f6 = "./files/参数化.yml"
-
print(YamlUtil().yaml_util(f6, {"username": "测试张三"}))
-
f7 = "./files/多文档.yml"
-
for i in YamlUtil().multiple(f7):
-
print(i)
总结:
感谢每一个认真阅读我文章的人!!!
作为一位过来人也是希望大家少走一些弯路,如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。
软件测试面试文档
我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
视频文档获取方式:
这份文档和视频资料,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!以上均可以分享,点下方小卡片即可自行领取。