简介
当我们做测试时,需要对一个场景不同的输入做验证的情况。
比如对于登录的测试,需要验证如下场景:
- 用户名、密码、验证码都正确
- 用户名不正确、密码正确、验证码正确
- 用户名、密码正确,验证码不正确
- …等等这些情况
对于执行的测试用例步骤相同、而数据不同 ,每次运行用例只变化的是数据 ,可以将这些数据专门放在一起进行批量循环运行 ,从而完成测试用例执行的目的 。
pytest中可以使用pytest.mark.parametrize()来实现以上的功能
参数化使用
使用装饰器 pytest.mark.parametrize 来实现参数化
看下pytest.mark.parametrize的传参都有啥以及是干啥的
有5个参数
argnames: 指定测试函数中需要的参数名,可以传如逗号分隔的字符串或字符串列表
argvalues: 要传递给测试函数的参数值, 可以传入元组或者列表。对于每个参数名,argvalues 中的元素应该按照参数的顺序一一对应
indirect: 这是一个布尔值,默认为 False。当设置为 True 时,pytest 会尝试执行传递进来的参数(即 argnames 指定的参数)作为函数。这在某些情况下很有用,比如当参数名实际上是一个函数名时。
ids: 这是一个可选的字符串列表,用于为每个测试用例提供一个自定义的 ID。这有助于在测试报告中更清晰地标识每个测试用例。需要注意的是,ids 的长度需要与测试用例的数量一致。
scope: 工作中没用过这里不做介绍
单参数使用
import pytest
@pytest.mark.parametrize("a", [1, 2])
def test_parametrize(a):
print(a)
执行结果
多参数使用
@pytest.mark.parametrize("a, b", [(1, "a"), (2, "b")])
def test_two_parametrize(a, b):
print("a: ", a)
print("b: ", b)
执行结果
indirect参数
- 默认 indirect 值是False
- 若设置成True,表示把被parametrize修饰器修饰的方法形参当函数执行(parametrize中参数名和这个形参同名),此时必须有被@pytest.fixture()修饰的和形参名同名的函数(可以对参数做一些加工处理),否则报错:fixture ‘xxx’ not found,xxx表示形参名;简单说,为True时,形参被当成是一个fixture函数
验证默认 indirect 值是False
@pytest.mark.parametrize("login", [{"user": "zhangsan", "password": "123456"}, {"user": "lisi", "password": "123"}],
indirect=False)
def test_login(login):
print("user:", login['user'])
print("pwd:", login["password"])
将 indirect 改为 True
可以看到报错提示 没找到 login这个fixture
所以 如果 indirect 改为 True 则需要实现和参数名称一样的fixtrue函数
@pytest.fixture()
def login(request):
"""验证是否可以登录"""
print(request.param)
user = request.param['user']
pwd = request.param["password"]
print("user:", user)
print("pwd:", pwd)
if user == "zhangsan" and pwd == "123456":
return True
else:
return False
@pytest.mark.parametrize("login", [{"user": "zhangsan", "password": "123456"}, {"user": "lisi", "password": "123"}],
indirect=True)
def test_login(login):
# print("user:", login['user'])
# print("pwd:", login["password"])
print("login: ", login)
if login:
print("login 成功....")
else:
print("login 失败....")
Ids 参数
当使用参数化的时候, 如果在case中没有详细输出参数的值, 在case运行中不好知道每条case执行的情况是啥
那么这里可以使用ids这个参数来标明每组参数对应的想测试的情况是啥
ids 传参是一个列表, 列表的长度要和参数化的case数量一致
@pytest.fixture()
def login(request):
"""验证是否可以登录"""
print(request.param)
user = request.param['user']
pwd = request.param["password"]
print("user:", user)
print("pwd:", pwd)
if user == "zhangsan" and pwd == "123456":
return True
else:
return False
@pytest.mark.parametrize("login", [{"user": "zhangsan", "password": "123456"}, {"user": "lisi", "password": "123"}],
indirect=True, ids=["correct login", "error not login "])
def test_login(login):
# print("user:", login['user'])
# print("pwd:", login["password"])
print("login: ", login)
if login:
print("login 成功....")
else:
print("login 失败....")
注意 这里如果ids输入中文,结果中会展示成unicode码 看下边效果🔽
@pytest.mark.parametrize("login", [{"user": "zhangsan", "password": "123456"}, {"user": "lisi", "password": "123"}],
indirect=True, ids=["账户密码正确", "账户密码错误"])
def test_login(login):
# print("user:", login['user'])
# print("pwd:", login["password"])
print("login: ", login)
if login:
print("login 成功....")
else:
print("login 失败....")
如何解决
在项目根目录下建一个pytest.ini 文件 配置如下内容
[pytest]
disable_test_id_escaping_and_forfeit_all_rights_to_community_support = True
或者在conftest.py 文件中
def pytest_collection_modifyitems(items):
for item in items:
item.name = item.name.encode("utf-8").decode("unicode_escape")
item._nodeid = item.nodeid.encode("utf-8").decode("unicode_escape")
修改后