六、yaml数据驱动

本文介绍了Yaml数据格式的基本概念、语法规则,包括字典、列表及其嵌套的表示方法,并展示了在Python中如何进行Yaml文件的读写。此外,文章讨论了Yaml在数据驱动应用中的作用,特别是在自动化测试中的使用,强调了数据与测试脚本的对应关系及解析方法。
摘要由CSDN通过智能技术生成

一. Yaml 数据格式

应用场景
概念和语法规则
Yaml 是一种所有编程语言可用的友好的数据序列化标准。语法和其他高阶语言类似,并且可以简单表达字典、列表
和其他基本数据类型的形态。语法规则如下:

  1. 大小写敏感。
  2. 使用缩进表示层级关系。
  3. 缩进时不允许使用Tab键,只允许使用空格。
  4. 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
    快速体验
    将一个字典 {“name”: “xiaoming”, “age”, “18”} 写成 Yaml 的形式,并输入结果

data.yaml

name: "xiaoming"
age: "18"

demo.py

import yaml
with open("./data.yaml",'r') as f:
data = yaml.load(f)
print(data)

1.1 字典和列表
应用场景
使用的数据需要使用列表和字典展示的时候,也同时为后续复杂的数据做铺垫
1.1.1 字典
需求:
使用 Yaml 编写 {“name”: “xiaoming”, “age”, “18”

name: "xiaoming"
age: "18"

1.1.2 列表
需求:
使用 Yaml 编写 [“1”, “2”, “3”]

- "1"
- "2"
- "3"

小结
字典直接写key和value,每一个键值对占一行。
列表中的元素需要用 “-” 来表示,每一个元素前面都有一个 “-”,后面接元素内容

1.2 字典和列表相互嵌套
应用场景
真实的数据一般不会是一个单纯的字典和列表,掌握了相互的嵌套,再复杂的数据也能看懂。

1.2.1 字典嵌套字典
需求:
使用 Yaml 编写 {people1: {“name”: “xiaoming”, “age”, “18”}, people2: {“name”: “xiaohong”, “age”, “20”}}

peopel1:
name: "xiaoming"
age: "18"
peopel2:
name: "xiaohong"
age: "20"

1.2.2 字典嵌套列表
使用 Yaml 编写 {people1: [“1”, “2”, “3”]}

peopel1:
- "1"
- "2"
- "3"

1.2.3 列表嵌套字典
使用 Yaml 编写 [{“name”: “xiaoming”, “age”, “18”}, {“name”: “xiaohong”, “age”, “20”}]

-
peopel1:
name: "xiaoming"
age: "18"
-
peopel2:
name: "xiaohong"
age: "20"

1.2.4 列表嵌套列表
使用 Yaml 编写 [[“1”, “2”, “3”], [“4”, “5”, “6”]]

-
	- "1"
	- "2"
	- "3"
-
	- "4"
	- "5"
	- "6"

强化训练:
使用 Yaml 编写:
[“1”, “2”, {“name”: [“xiaoming”, “xiaohong”], “age”: “18”}, [{“name”: “xiaoqiang”, “age”: “28”}, “3”, “4”], “5”,[“7”, “8”]]

- "1"
- "2"
-
	name:
		- "xiaoming"
		- "xiaohong"
	age: "18"
-
	-
		name: "xiaoqiang"
		age: "28"
	- "3"
	- "4"
- "5"
-
	- "7"
	- "8"

注意点:
pycharm会自动将yaml中的tab转化成空格。但是不能复制”tab键“,不会进行转化。同级别左侧必须要对齐,少个空格无所谓。

1.3 其他基本数据类型

应用场景
工作中的 yaml 除了最基本的字符串,还可能要使用其他的一些数据类型。学习之后,我们就可以编写包含其他数据类型的 yaml 文件了。同时,也可以知道其中部分数据类型的注意点。

1.3.1 整数
使用 Yaml 展示 整数

"整数1": 1
"整数2": 25

1.3.2 浮点数
使用 Yaml 展示 浮点数

"浮点数1": 1.8
"浮点数2": 20.6

1.3.3 布尔类型
使用 Yaml 展示 布尔类型

"布尔类型1": True
"布尔类型2": TRUE
"布尔类型3": true
"布尔类型4": False
"布尔类型5": FALSE
"布尔类型6": false

1.3.4 空值
使用 Yaml 展示 空值

"空值1": Null
"空值2": NULL
"空值3": null

1.3.5 时间
使用 Yaml 展示 时间

"时间": 2019-03-25 05:28:34.2345

1.3.6 字符串
使用 Yaml 展示 字符串

"字符串1": "hello"
"字符串2": hello

二. Yaml 在 python 中的读写

应用场景

yaml 的读写在后续的数据驱动应用中起到不可或缺的作用。掌握yaml的读写不仅可以用于测试,如果以后大家想从
事 python 的工作,也会可能会使用到。

2.1 读取 Yaml 文件
demo_load.py

import yaml
# 打开要读取的文件
with open("./data.yaml", 'r') as f:
# 加载文件内容
data = yaml.load(f)
# 打印结果
print(data)

小结
读取实际上就是先使用 with open 的形式获取文件对象,再使用 yaml 中的 load 方法加载这个文件对象

注意点
在 windows 系统中,去读中文可能有问题,需要在 with open 中增加参数 encoding=‘utf-8’

import yaml
# 打开要读取的文件
with open("./data.yaml", 'r', encoding='utf-8') as f:
# 加载文件内容
data = yaml.load(f)
# 打印结果
print(data)

2.2 写入 Yaml 文件
demo_dump.py

import yaml
# 准备data数据
data = {'search_data': {'search_test_002': {'expect': {'value': 'hello'}, 'value': 'hello'},
'search_test_001': {'expect': [4, 5, 6], 'value': 456}}}
# 将data写入
with open("./data_dump.yaml", "w") as f:
yaml.dump(data, f)

data_dump.yaml

search_data:
search_test_001:
expect: [4, 5, 6]
value: 456
search_test_002:
expect: {value: hello}
value: hello

问题

我们会发现在写入的时候,如果将内容换成中文,那么中文的显示是有问题的。此时我们应该设置一下写入的编码。

解决方案
demo_dump.py

import yaml
# 准备data数据
data = {search_data': {'search_test_002': {'expect': {'value': '你好'}, 'value': '你好'},
'search_test_001': {'expect': [4, 5, 6], 'value': 456}}}
# 将data写入
with open("./data_dump.yaml", "w") as f:
yaml.dump(data, f, encoding='utf-8', allow_unicode=True)

小结
写入实际上就是先使用 with open 的形式获取文件对象,再使用 yaml 中的 dump 方法加载数据和这个文件对象

三. Yaml 数据驱动应用

所谓数据驱动应用,就是将我们在自动化测试脚本的使用的数据部分不在脚本中 “写死” 而是通过yaml的形式进行展现。以后如果遇到对数据需求的更变,也可以快速的进行修改。

3.1 项目需求
项目需求
使用 pytest 框架 + po 模式 + yaml 数据 完成通讯里添加联系人案例。

3.2 项目准备
使用 pytest 框架、po 模式 打开要测试的通讯里程序
pytest.ini

[pytest]
addopts = -s --reruns 0
testpaths = ./scripts
python_files = test_*.py
python_classes = Test*
python_functions = test_*

page/new_contact_page.py

from selenium.webdriver.common.by import By
from base.base_action import BaseAction
class NewContactPage(BaseAction):
# 姓名输入框
name_edit_text = By.XPATH, "//*[@text='姓名']"
# 电话输入框
phone_edit_text = By.XPATH, "//*[@text='电话']"
# 输入姓名
def input_name(self, text):
self.input(self.name_edit_text, text)
# 输入电话
def input_phone(self, text):
self.input(self.phone_edit_text, text)

page/contact_list_page.py

from selenium.webdriver.common.by import By
from base.base_action import BaseAction
class ContactListPage(BaseAction):
# 添加联系人
add_contact_button = By.ID, "com.android.contacts:id/floating_action_button"
# 点击添加联系人
def click_add_contact(self):
self.click(self.add_contact_button)

page/page.py

from page.contact_list_page import ContactListPage
from page.new_contact_page import NewContactPage
class Page:
def __init__(self, driver):
self.driver = driver
@property
def contact_list(self):
return ContactListPage(self.driver)
@property
def new_contact(self):
return NewContactPage(self.driver)

scripts/test_contact.py

import time
import pytest
from base.base_driver import init_driver
from page.page import Page
class TestContact:
def setup(self):
self.driver = init_driver()
self.page = Page(self.driver)
def teardown(self):
time.sleep(5)
self.driver.quit()
@pytest.mark.parametrize(("name", "phone"), [("zhangsan", "18888888888"), ("lisi",
"13333333333"), ("wangwu", "17777777777")])
def test_contact(self, name, phone):
# 主页 - 点击 新建联系人
self.page.contact_list.click_add_contact()
# 新建联系人 - 输入 姓名
self.page.new_contact.input_name(name)
# 新建联系人 - 输入 电话
self.page.new_contact.input_phone(phone)

3.3 数据驱动应用

3.3.1 需求

将 scripts/test_contact.py 中的 @pytest.mark.parametrize((“name”, “phone”), [(“zhangsan”,“18888888888”), (“lisi”,“13333333333”), (“wangwu”, “17777777777”)]) 的数据部分改为 yaml 形式编写

3.3.2 yaml 数据和脚本之间的对应关系

分析

我们在实际工作中,不同的项目可能会遇到很多有变化的情况,以下来列举这些 “变数”

  • 一个项目有多个 “模块” 都需要使用数据
  • 一个模块有多个 “测试脚本” 都需要使用数据
  • 一个测试脚本有多个 “用例” 都需要使用数据

其实,一个项目无非就是这些 “变数” ,只要大家掌握yaml数据和这些“变数”的对应关系,那么就不难了。

举例:
接下来我们来用测试用例和 yaml 数据最终的结果进行一个对比。
在这里插入图片描述
yaml数据如下:
login_data.yaml

test_login:
test_login_001:
username: "zhangsan"
password: "zhangsan123"
test_login_002:
username: "lisi"
password: "lisi123"
test_login_003:
username: "wangwu"
password: "wangwu123"

sign_up_data.yaml

test_username_sign_up:
test_username_sign_up_001:
username: "zhangsan"
password: "zhangsan321"
test_username_sign_up_002:
username: "lisi"
password: "lisi321"
test_phone_sign_up:
test_phone_sign_up_001:
phone: "13333333333"
password: "123000"
test_phone_sign_up_002:
phone: "18888888888"
password: "321000"

结论
一个 yaml 数据文件 对应 一个模块

  • sign_up_data.yaml 对应 注册
  • login_data.yaml 对应 登录

数据内容中最外层 key 对应 一个模块下的脚本名

  • test_username_sign_up 对应 函数test_username_sign_up
  • test_phone_sign_up 对应 函数 test_phone_sign_up
    数据内容中第二层 key 对应 用例编号
  • test_username_sign_up_001 对应 编号 test_username_sign_up_001
  • test_username_sign_up_002 对应 编号 test_username_sign_up_002
    数据内容中最里层 key 对应 用例的具体数据
  • username 对应 用户名
  • password 对应 密码

通讯里中的数据
contact_data.yaml

test_contact:
test_contact_001:
name: "zhangsan"
phone: "18888888888"
test_contact_002:
name: "lisi"
phone: "13333333333"
test_contact_003:
name: "wangwu"
phone: "17777777777"

3.3.3 yaml 数据的解析

分析

装饰器 @pytest.mark.parametrize 中,接收两个参数,第一个为参数名,第二个为列表。我们需要单独写一套解析方法即可,为了后期也可以解析其他的文件,我们直接将解析内容写到 base 中即可。

代码
base/base_analyze.py

import os
import yaml
def analyze_file(file_name, key):
with open(".%sdata%s%s" % (os.sep, os.sep, file_name), "r") as f:
case_data = yaml.load(f)[key]
data_list = list()
for i in case_data.values():
data_list.append(i)
return data_list

3.3.4 调整测试脚本
scripts/test_contact.py

import time
import pytest
from base.base_analyze import analyze_file
from base.base_driver import init_driver
from page.page import Page
class TestContact:
def setup(self):
self.driver = init_driver()
self.page = Page(self.driver)
def teardown(self):
time.sleep(5)
self.driver.quit()
@pytest.mark.parametrize("args", analyze_file("contact_data.yaml", "test_contact"))
def test_contact(self, args):
name = args["name"]
phone = args["phone"]
# 主页 - 点击 新建联系人
self.page.contact_list.click_add_contact()
# 新建联系人 - 输入 姓名
self.page.new_contact.input_name(name)
# 新建联系人 - 输入 电话
self.page.new_contact.input_phone(phone)

3.3.5 增加断言

脚本并不是执行完了之后就算是通过。而是执行完后效果也正确,才能算通过。我们应该再最后增加一个断言的判断。

思路

输入完姓名和电话后,点击返回。会展示保存的联系人页面,如果这个页面上展示的是我们刚刚存的联系人姓名,就可以认为是成功的。

步骤

  1. 在添加联系人的页面点击返回
    在base中封装一个发送按键到手机的方法
  2. 创建保存的联系人页面对应的文件
  3. 在这个文件中写一个获取标题的方法
    resource-id 为 “com.android.contacts:id/large_title”
  4. 断言传入的名字和获取的名字是否相同

核心代码
page/saved_contact_page.py

from selenium.webdriver.common.by import By
from base.base_action import BaseAction
class SavedContactPage(BaseAction):
# 姓名输入框
name_title_feature = By.ID, "com.android.contacts:id/large_title"
def get_name_title_text(self):
return self.find_element(self.name_title_feature).text

page/page.py

from page.contact_list_page import ContactListPage
from page.new_contact_page import NewContactPage
from page.saved_contact_page import SavedContactPage
class Page:
def __init__(self, driver):
self.driver = driver
@property
def contact_list(self):
return ContactListPage(self.driver)
@propert
def new_contact(self):
return NewContactPage(self.driver)
@property
def saved_contact_page(self):
return SavedContactPage(self.driver)

scripts/test_contact.py

import time
import pytest
from base.base_analyze import analyze_file
from base.base_driver import init_driver
from page.page import Page
class TestContact:
def setup(self):
self.driver = init_driver()
self.page = Page(self.driver)
def teardown(self):
time.sleep(5)
self.driver.quit()
@pytest.mark.parametrize("args", analyze_file("contact_data.yaml", "test_contact"))
def test_contact(self, args):
name = args["name"]
phone = args["phone"]
# 主页 - 点击 新建联系人
self.page.contact_list.click_add_contact()
# 新建联系人 - 输入 姓名
self.page.new_contact.input_name(name)
# 新建联系人 - 输入 电话
self.page.new_contact.input_phone(phone)
# 断言:name 是否和 获取的name一致
assert name == self.page.saved_contact_page.get_name_title_text()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值