05. 参数化
2024年10月28日大约 4 分钟
05. 参数化
1. 参数化引入案例
- 需求
- 定义一个实现加法操作的函数,并对该函数进行测试要求对多组测试数据进行测试
import unittest
class TestAdd(unittest.TestCase):
def test_add_01(self):
result = add(1, 1)
self.assertEqual(result, 2)
def test_add_02(self):
result = add(1, 0)
self.assertEqual(result, 1)
def test_add_03(self):
result = add(0, 0)
self.assertEqual(result, 0)
def test_add(self):
test_data = [(1, 1, 2), (1, 0, 1), (0, 0, 0)]
for x, y, expect in test_data:
print("x={} y={} expect={}".format(x, y, expect))
result = add(x, y)
self.assertEqual(result, expect)
发现问题
一条测试数据定义一个测试函数,代码冗余度太高
一个测试函数中测试多条数据,最终只会有一个测试用例结果(一个用例方法一个测试结果,for循环在测试方法中)
2. 参数化介绍
说明
通过参数的方式来传递数据,从而实现数据和脚本分离。并且可以实现用例的重复执行
unittest测试框架,本身不支持参数化,但是可以通过安装 unittest 扩展插件 parameterized 来实现
安装parameterized
- pip install parameterized
pip介绍
- pip是一个Python包管理工具,提供了对Python包的查找、下载、安装、卸载等功能
安装包
- pip install 包名
- 安装包指定版本
- pip install 包名 ==版本号
- 例如:pip install matplotlib==3.4.1
- pip install 包名 ==版本号
- 指定国内源来安装
- pip install -i https://pypi.douban.com/simple/ package_name
更新指定包
- pip install --upgrade package_name # 或者 pip install -U package_name
查看包
- pip show 包名
- 查看包信息
- pip show -f 包名
卸载包
- pip uninstall 包名
列出已安装的包
- pip list
pip freeze 将系统已安装的包按照需要的格式打包
- pip freeze > requirements.txt (重定向,并且指定输出文件名称、格式、路径)
批量安装
- pip install -r requirements.txt
3. 参数化实现
- 使用方式
- 导包:from parameterized import parameterized
- 使用@parameterized.expand装饰器可以为测试函数的参数进行参数化
- 数据格式:元组类型的列表 [(),(),(),……]
# 方式一
@parameterized.expand([(1, 1, 2), (1, 0, 1), (0, 0, 0)])
def test_add(self, x, y, expect):
pass
# 方式二
data = [(1, 1, 2), (1, 0, 1), (0, 0, 0)]
@parameterized.expand(data)
def test_add(self, x, y, expect):
pass
# 方式三
def build_data():
return [(1, 1, 2), (1, 0, 1), (0, 0, 0)]
@parameterized.expand(build_data)
def test_add(self, x, y, expect):
pass
import unittest
from parameterized import parameterized
# 构建测试数据
def build_data():
return [(1, 1, 2), (1, 0, 1), (0, 0, 0)]
class TestAdd(unittest.TestCase):
# 参数化方式一
@parameterized.expand([(1, 1, 2), (1, 0, 1), (0, 0, 0)])
def test_add_1(self, x, y, expect):
print("x={} y={} expect={}".format(x, y, expect))
result = add(x, y)
self.assertEqual(result, expect)
# 参数化方式二
data = [(1, 1, 2), (1, 0, 1), (0, 0, 0)]
@parameterized.expand(data)
def test_add_2(self, x, y, expect):
print("x={} y={} expect={}".format(x, y, expect))
result = add(x, y)
self.assertEqual(result, expect)
# 参数化方式三
@parameterized.expand(build_data)
def test_add_3(self, x, y, expect):
print("x={} y={} expect={}".format(x, y, expect))
result = add(x, y)
self.assertEqual(result, expect)
if __name__ == '__main__':
unittest.main()
4. 演示
// data/add_data.json
[
[
1,
1,
2
],
[
1,
2,
3
],
[
2,
3,
5
],
[
4,
5,
9
],
[
10,
20,
30
]
]
// data/add_data_1.json
[
{
"a": 1,
"b": 2,
"expect": 3
},
{
"a": 11,
"b": 22,
"expect": 33
},
{
"a": 12,
"b": 23,
"expect": 35
},
{
"a": 14,
"b": 25,
"expect": 39
}
]
# app.py
import os
import sys
# 获取当前文件的绝对路径:abspath = os.path.abspath(file)
# 获取文件路径的目录名称:dirname = os.path.dirname(filepath)
# __file__ 特殊的变量,表示当前代码文件名
path1 = os.path.abspath(__file__)
print('path1 = ', path1)
path2 = os.path.dirname(path1)
print('path2 = ', path2)
# sys.path python模块在搜索路径列表
sys.path.append(os.path.dirname(__file__))
print('sys.path = ', sys.path, end='\n')
print('os.path.dirname(__file__) = ', os.path.dirname(__file__), end='\n')
# BASE_DIR = os.path.dirname(os.path.abspath(__file__))
BASE_DIR = os.path.dirname(__file__)
if __name__ == '__main__':
print('BASE_DIR = ', BASE_DIR)
# common/read_data.py
import json
from test_code.aa_unitest.level_b.app import BASE_DIR
def build_add_data():
with open('../data/add_data.json') as f:
data = json.load(f) # [[], [], []] ---> [(), ()]
return data
def build_add_data_1():
with open('../data/add_data_1.json') as f:
data_list = json.load(f) # [{}, {}, {}] ----> [(), ()]
new_list = []
for data in data_list: # data 字典
# 字典中的值,是否都需要
a = data.get('a')
b = data.get('b')
expect = data.get('expect')
new_list.append((a, b, expect))
# new_list.append([a, b, expect])
return new_list
def build_add_data_2():
with open(BASE_DIR + '/data/add_data_1.json') as f:
data_list = json.load(f) # [{}, {}, {}] ----> [(), ()]
new_list = []
for data in data_list: # data 字典
# 字典中的值,是否都需要
new_list.append(tuple(data.values()))
return new_list
def build_login_data():
with open(BASE_DIR + '/data/login_data.json', encoding='utf-8') as f:
data_list = json.load(f) # [{}, {}] ---> [()]
new_list = []
for data in data_list:
# 字典中的 desc 不需要
username = data.get('username')
password = data.get('password')
expect = data.get('expect')
new_list.append((username, password, expect))
return new_list
if __name__ == '__main__':
# print(build_add_data())
# print(build_add_data_1())
print(build_add_data_2())
# hm_04_pa1.py
import unittest
from parameterized import parameterized
from common.read_data import build_add_data_2
from tools import add
data = [(1, 1, 2), (1, 2, 3), (2, 3, 5), (4, 5, 9)]
class TestAdd(unittest.TestCase):
"""测试"""
# @parameterized.expand(build_add_data()) # 修改路径 .. -> .
# @parameterized.expand(build_add_data_1()) # 修改路径 .. -> .
@parameterized.expand(build_add_data_2())
def test_add(self, a, b, expect):
f"""测试a:{a}, b:{b}, expect:{expect}"""
print(f'a:{a}, b:{b}, expect:{expect}')
self.assertEqual(expect, add(a, b))
if __name__ == '__main__':
unittest.main()