转载请注明出处❤️
作者:测试蔡坨坨
原文链接:caituotuo.top/90d52d38.html
你好,我是测试蔡坨坨。
众所周知,我们在做自动化测试的时候,通常会把配置信息和测试数据存储到特定的文件中,以实现数据和脚本的分离,从而提高代码的易读性和可维护性,便于后期优化。而配置文件的形式更是多种多样,比如:ini、yaml、json、toml、py、xml、properties等。
在之前介绍的接口和Web UI自动化测试框架 ——「五分钟学会接口自动化测试框架」和「五分钟搞懂 POM 设计模式」中都是通过yaml文件进行配置信息和测试数据的管理,所以今天我们就来聊一聊YAML数据驱动。
1 什么是YAML
YAML:YAML Ain’t a Markup Language,翻译过来就是「YAML不是一种标记语言」。
但是在开发这种语言时,YAML的意思其实是Yet Another Markup Language「仍是一种标记语言」。
它是一种以数据为中心的标记语言,比 XML 和 JSON 更适合作为配置文件。
YAML的配置文件后缀为.yml
或.yaml
,如:caituotuo.yml或caituotuo.yaml。
YAML的语法和其他高级语言类似,并且可以简单表达清单、散列表,标量等数据形态。它使用空白符号缩进和大量依赖外观的特色,特别适合用来表达或编辑数据结构、各种配置文件、倾印调试内容、文件大纲等。
2 YAML语法
2.1 基本语法
- 使用缩进表示层级关系
- 缩进不允许使用tab,只允许空格(官方说法不允许使用tab,当然如果你使用tab在某些地方也是可以的,例如在PyCharm软件上)
- 缩进的空格数不重要,只要相同层级的元素左对齐即可
- 大小写敏感
- 前面加上#表示注释
例如:
1 2 3 4 5 6 7 8
| req: username: 测试蔡坨坨 gender: Boy ip: 上海 blog: caituotuo.top res: status: 1 code: 200
|
2.2 数据类型
- 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
- 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
- 纯量(scalars):单个的、不可再分的值,又称字面量
纯量
纯量是指单个的,不可拆分的值,例如:数字、字符串、布尔值、Null、日期等,纯量直接写在键值对的value中即可。
字符串:
默认情况下字符串是不需要使用单引号或双引号的
当然使用双引号或者单引号包裹字符也是可以的
1 2
| username: 'Hello world 蔡坨坨' username: "Hello world 蔡坨坨"
|
字符串可以拆成多行,每一行会被转化成一个空格
布尔值:
1 2 3 4 5
| boolean: - TRUE - FALSE
|
数字:
1 2 3 4 5 6 7 8
| float: - 3.14 - 6.8523015e+5 int: - 123 - 0b1010_0111_0100_1010_1110
|
Null:
1 2 3 4 5 6 7
| null: nodeName: 'node' parent: ~ parent2: None parent3: null
|
时间和日期:
1 2 3 4 5 6
| date: - 2018-02-17 datetime: - 2018-02-17T15:02:31+08:00
|
对象
使用key:[空格]value
的形式表示一对键值对(空格不能省略),例如:blog: caituotuo.top
。
行内写法:
1
| key: {key1: value1, key2: value2, ...}
|
普通写法,使用缩进表示对象与属性的层级关系:
1 2 3
| key: child-key: value child-key2: value2
|
数组
以 -
开头的行表示构成一个数组。
普通写法:
1 2 3 4
| name: - 测试蔡坨坨 - 小趴蔡 - 蔡蔡
|
YAML 支持多维数组,可以使用行内表示:
1
| key: [value1, value2, ...]
|
数据结构的子成员是一个数组,则可以在该项下面缩进一个空格:
1 2 3 4 5 6 7 8 9 10 11
| username: - - 测试蔡坨坨 - 小趴蔡 - 蔡蔡 - - A - B - C
|
相对复杂的例子:
companies 属性是一个数组,每一个数组元素又是由 id、name、price 三个属性构成
1 2 3 4 5 6 7 8 9 10 11
| companies: - id: 1 name: caituotuo price: 300W - id: 2 name: 测试蔡坨坨 price: 500W
|
数组也可以使用flow流式的方式表示:
1
| companies2: [ { id: 1,name: caituotuo,price: 300W },{ id: 2,name: 测试蔡坨坨,price: 500W } ]
|
复合结构
以上三种数据结构可以任意组合使用,以实现不同的用户需求,例如:
1 2 3 4 5 6 7 8 9 10
| platform: - 公众号 - 小红书 - 博客 sites: 公众号: 测试蔡坨坨 小红书: 测试蔡坨坨 blog: caituotuo.top
|
3 引用
&
锚点和 *
别名,可以用来引用。
举个栗子:
& 用来建立锚点defaults,<< 表示合并到当前数据,* 用来引用锚点
1 2 3 4 5 6 7 8 9 10 11
| defaults: &defaults adapter: postgres host: localhost
development: database: myapp_development <<: *defaults
test: database: myapp_test <<: *defaults
|
等价于:
1 2 3 4 5 6 7 8 9 10 11 12 13
| defaults: adapter: postgres host: localhost
development: database: myapp_development adapter: postgres host: localhost
test: database: myapp_test adapter: postgres host: localhost
|
4 组织结构
一个YAML文件可以由一个或多个文档组成,文档之间使用---
作为分隔符,且整个文档相互独立,互不干扰,如果YAML文件只包含一个文档,则---
分隔符可以省略。
1 2 3 4 5 6 7 8 9 10
| --- website: name: 测试蔡坨坨 url: caituotuo.top --- website: { name: 测试蔡坨坨,url: caituotuo.top } --- 公众号: 测试蔡坨坨 --- 小红书: 测试蔡坨坨
|
1 2 3 4 5
| f7 = "./files/多文档.yml" with open(f7, "r", encoding="UTF-8") as f: content = yaml.safe_load_all(f) for i in content: print(i)
|
1 2 3 4 5 6
| 运行结果:
{'website': {'name': '测试蔡坨坨', 'url': 'caituotuo.top'}} {'website': {'name': '测试蔡坨坨', 'url': 'caituotuo.top'}} {'公众号': '测试蔡坨坨'} {'小红书': '测试蔡坨坨'}
|
5 实战
封装思路
将YAML相关操作封装成CommonUtil公共模块,之后直接引入调用即可。
相关功能:
- 读取yaml文件数据
- 将yaml数据转换成json格式
- 可以动态设置参数
这里要说一下动态设置参数
在自动化测试中,肯定不能把所有的参数都写死,因此就会用到参数化,例如:提取前一个接口的返回值作为后一个接口的入参,这里通过Python中的Template模块进行动态参数的设置
yaml文件中通过$变量名
的形式设置变量
给变量附上具体的值
1 2 3 4
| with open(yaml_path, "r", encoding="UTF-8") as f: text = f.read()
Template(text).safe_substitute({"username": "测试蔡坨坨"})
|
完整代码
源码获取请关注公众号:测试蔡坨坨
,回复关键词:源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
|
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)
|