使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 22--数据驱动--参数化处理 Json 文件
测试学习记录,仅供参考!
数据驱动
结合 pytest 框架参数化处理对测试用例做参数化;这里介绍 Json 格式文件、yaml 格式文件、Excel 格式文件 三种文件方式去做测试用例参数化;自行根据喜好选择一种做参数化;
一、使用Json文件对测试用例做参数化
JSON是一种轻量级的数据交互格式,采用完全独立于编程语言的文本格式来存储和表示数据,本质上是一个带有特定格式的字符串。JSON负责不同编程语言中的数据传递和交互,类似国际通用语言中的英语。
各种编程语言存储数据的容器不尽相同,在Python中有字典dict这样的数据类型,而其它语言可能没有对应的字典。为了让不同的语言都能够相互通用的互相传递数据,JSON就是一种非常良好的中转数据格式。
JSON数据的格式
基本结构
JSON是一个键值对(key/value)的集合。
键:必须是字符串类型
值:可以是字符串、数字、布尔值、数组、其他对象(即嵌套的键值对集合)以及null
有两种基本结构,对象(Object)和数组(Array)。
键值对集合(对象):表示为{"key":value}
值的有序列表(数组):表示为[value1,value2]
数据类型
JSON支持以下几种数据类型:字符串(String)、数字(Number)、对象(Object)、数组(Array)、布尔值(true/false)、null
格式规范
必须以开头的{或[开始,并以对应的} 或 ] 结尾
所有的键名必须是双引号括起来的字符串
数组和对象中的元素或成员之间用逗号 ,分隔,最后一个元素或成员后面可以有或没有逗号,但建议不加,以保持一致性。
JSON格式数据转化
序列化
使用json.dumps()函数可以将Python对象序列化为JSON格式的字符串。
反序列化
使用json.loads()函数可以将JSON格式的字符串反序列化为Python对象。
注意事项
JSON字符串必须使用双引|号,单引号无效
Python的json 模块默认不处理中文ASCll编码,需要ensure_ascii=False
JSON没有日期类型,日期通常转为字符串处理
JSON不支持注释
尾随逗号在JSON中无效(如{"a":1,})
代码展示
1、在项目根目录 data 目录文件下新建名称为 login.json 的 json 文件,注意格式,里面要带列表,因为 pytest 框架参数化要带有可迭代的对象;需注意 json 文件中的引号要是 "2" 双引号,若是 '1' 单引号的话则会报错;

2、数据格式最外层是列表,里面嵌套字典;
[{"username": "admin123","password": "123456"},{"username": "admin123456","password": "123456"},{"username": "admin123","password": "123456789"},{"username": "","password": ""}
]
3、写了 json 格式的数据后,得去写一个文件去读取 json 的数据;
4、在项目根目录 util_tools 软件包下新建名称为 handle_data(处理数据的类都放在这下面) 的软件包,在 handle_data 软件包下新建名称为 operateJson.py(处理 json 文件) 的 Python 文件;专门处理 json 文件;
json 文件读取比较简单,第一步,把 json 模块导进来,这里不需要再封装类;
直接自定义一个函数 def read_json() 里面传一个参数(读取哪个 json 文件的路径);
一个点‘.’代表当前文件;
一个点加一个斜杠‘./’代表当前文件的目录;
两个点‘..’代表当前目录的上一级目录;
点(.)的含义:
- 点(.)代表当前目录。它是一个特殊的符号,用于指示当前工作目录的位置。
- 例如,当你执行
cd .命令时,实际上并没有改变当前目录,因为.就是当前目录的指代。
双点(..)的含义:
- 双点(..)代表当前目录的上一级目录,也就是父目录。它允许你在文件系统中向上导航。
import jsondef read_json(file_path):"""读取json的数据:param file_path: 文件路径:return:"""with open(file_path, 'r', encoding='utf-8') as file:data = json.load(file)return data# 调试查看--注意相对路径和绝对路径的区别
res = read_json('../../data/login.json')
print(res)
5、 运行查看结果;
[{'username': 'admin123', 'password': '123456'}, {'username': 'admin123456', 'password': '123456'}, {'username': 'admin123', 'password': '123456789'}, {'username': '', 'password': ''}]进程已结束,退出代码为 0
6、改造读取 json 的文件,优化 operateJson.py 文件内容,增加列表推导式;
1)、[for data_dict in data]:列表推导式 for 循环读取 data_dict(字典) 去循环这个 data;
2)、[tuple() for data_dict in data]:通过 tuple() 这个去把它转换成元组;
3)、因为它 data_dict(自定义名称) 这个数据循环之后是如下一组数据,它是一个字典;
例如:{'username': 'admin123', 'password': '123456'}
4)、[tuple(data_dict.values()) for data_dict in data]:可以直接通过字典去读取它的 values 值;
5)、data_list_tuple = [tuple(data_dict.values()) for data_dict in data]:最终把结果给返回出去;
6)、return data_list_tuple:把结果返回出去;
import jsondef read_json(file_path):"""读取json的数据:param file_path: 文件路径:return: 返回列表类型,列表里面是元组数据,如:[('admin123','123456'),(),(),()......]"""with open(file_path, 'r', encoding='utf-8') as file:data = json.load(file)data_list_tuple = [tuple(data_dict.values()) for data_dict in data]return data_list_tuple# 调试查看--注意相对路径和绝对路径的区别
res = read_json('../../data/login.json')
print(res)
7、增加列表推导式运行成功之后可以查看结果,已经符合需要的数据格式要求;
对比 login.json 文件内容看,一组数据只抽取键值;
[('admin123', '123456'), ('admin123456', '123456'), ('admin123', '123456789'), ('', '')]进程已结束,退出代码为 0
8、把 json 文件的数据处理成符合要求的格式之后,可以在测试用例中做参数化了;
9、优化 operateJson.py 文件,注释一下或删除不需要的;
import jsondef read_json(file_path):"""读取json的数据:param file_path: 文件路径:return: 返回列表类型,列表里面是元组数据,如:[('admin123','123456'),(),(),()......]"""with open(file_path, 'r', encoding='utf-8') as file:data = json.load(file)data_list_tuple = [tuple(data_dict.values()) for data_dict in data]return data_list_tuple
10、优化 test_login.py 文件,添加参数化;
1)、 导包引入读取 json 文件的 def read_json(file_path): 方法,导入 pytest 库;
2)、使用 @pytest.mark.parametrize() 装饰器做参数化,第一个(参数)是接收后面一个可迭代参数(第二个参数)内容的参数,第一个参数 data(自定义名称),第二个参数 通过调用读取 json 文件 read_json() 方法,里面传路径文件参数,注意文件路径,在当前文件调试查看和在主函数 run.py 文件运行路径是不同的,因为最终是在主函数运行('./data/login.json');
3)、username, password = data :使用 username、password 两个变量接收元组解包数据;
data 是元组数据,[('admin123', '123456'), ('admin123456', '123456'), ('admin123', '123456789'), ('', '')]
4)、login_page.login(username, password):使用参数化数据传参;
import pytest
from selenium.webdriver.common.by import By
from time import sleep
from pageObject.login_page.login_page import LoginPage
from util_tools.handle_data.operateJson import read_jsonclass TestLogin:# 使用参数化实现一条测试用例跑多种场景--可迭代对象里面有多少组数据就跑多少次测试用例@pytest.mark.parametrize('data', read_json('./data/login.json'))# 用一个 data(需与上面保持一致) 参数去接收参数化数据def test_login_success(self, get_driver, data):# 进行元组解包--通过两个参数去接收元组中的每一组数据username, password = data# 传浏览器对象--再把结果返回login_page = LoginPage(get_driver)# 直接调用页面类中的 login 操作--里面需要输入两个参数(参数化传两个参数)login_page.login(username, password)# 断言结果success_ele = get_driver.find_element(By.XPATH, '//*[@id="ECS_MEMBERZONE"]/font/font')assert success_ele != ''sleep(2)
11、运作 run.py 主函数文件查看结果;
12、 添加‘异常处理’和‘日志打印’,优化 operateJson.py 文件;
import json
from util_tools.logs_util.recordlog import logsdef read_json(file_path):"""读取json的数据:param file_path: 文件路径:return: 返回列表类型,列表里面是元组数据,如:[('admin123','123456'),(),(),()......]"""try:with open(file_path, 'r', encoding='utf-8') as file:data = json.load(file)data_list_tuple = [tuple(data_dict.values()) for data_dict in data]return data_list_tupleexcept Exception as e:logs.error(f'读取Json文件异常,异常原因为:{e}')
补充:
参考文档官网:https://docs.python.org/zh-cn/3/library/json.html
四种方法 json.dumps json.dump json.loads json.load
json.dumps 编码:将字典或者列表转换为json格式的字符串。
json.loads 解码:将json格式字符串转换为python对象。且JSON对象必须是str、bytes或bytearray,而不是list。
json.dump :将字典或者列表转换为json格式的字符串并且写入到文件中。
json.load :从文件中读取json格式的字符串并且转换为python对象。
转换为字符串
json.dumps()
# 导包--导入库
import json
# json.dumps 可以将python列表转换成字符串
jsontr = json.dumps(['hello word',{'a':'b'}])
# 打印查看--转换成字符串有什么好处--可以把数据直接写入到文件里面
print(jsontr,type(jsontr)) # ["hello word", {"a": "b"}] <class 'str'>
# 使用open函数
with open('data_test.txt',mode='w',encoding='utf8') as f:# 使用write写入f.write(jsontr)
["hello word", {"a": "b"}] <class 'str'>进程已结束,退出代码为 0
运行成功后查看:

解析为Python格式
json.loads()
# 导包
import json
# 使用json.loads方法将 字符串 转化为 python 列表
jsonls = json.loads('["a","b","c",{"a":1,"b":2},{"a":2,"b":3},{"ab":"cd"}]')
# 打印
print(jsonls,type(jsonls))
['a', 'b', 'c', {'a': 1, 'b': 2}, {'a': 2, 'b': 3}, {'ab': 'cd'}] <class 'list'>进程已结束,退出代码为 0
json文件操作
写入json文件
示例:
# 导包
import json
# 写一个字典test_data
test_data = {"id":1,"test_name":["1,打开浏览器""2,输入账号密码"]
}
# 使用open函数--打开一个文件、权限 w、编码格式
with open('json_test.json', 'w',encoding='utf8') as f:# 存储文件--json.dump方法--文件体、文件流json.dump(test_data,f)
运行成功后查看:

json编码格式问题
# 导包
import json
# 写一个字典test_data
test_data = {"id":1,"test_name":["1-打开浏览器;""2-输入账号密码;"]
}
# 使用open函数--打开一个文件、权限 w、编码格式
with open('json_test.json', 'w',encoding='utf8') as f:# 存储文件--json.dump方法--文件体、文件流json.dump(test_data,f,ensure_ascii=False)
运行成功后查看:

读取json文件
示例:
# 导包
import json
# 使用open函数--打开一个文件、编码格式
with open('json_test.json',encoding='utf8') as f:# 加载文件--json.load方法--文件流datajs = json.load(f)# 打印并查看类型print(datajs, type(datajs))
ps:需要先写入内容,若json文件为空,则报错
{'id': 1, 'test_name': ['1,打开浏览器2,输入账号密码']} <class 'dict'>进程已结束,退出代码为 0
未完待续。。。
