我正在尝试将三个不同的固定装置传递给我的
pytest.mark.parameterize
装饰器,如下所示:
@pytest.mark.parametrize("credentials, return_code", [
(user1, 200),
#(user2, 200),
#(user3, 401)
])
def test_login():
assert True
# Actual test uses response.return_code == return_code
但是,我收到一条错误消息:
NameError: name 'user1' is not defined
但这并不正确,我已经在
conftest.py
中定义了它:
user1_info = [('user1', 'password')]
@pytest.fixture(params=user1_info)
def user1(request):
return request.param
如果我将测试更改为如下所示,则夹具可以工作:
def test_login(user1):
assert True
# Actual test uses response.return_code == return_code
问题是,如果我这样做,我必须编写三个单独的测试,一个用于每个固定装置,一个用于每个 status_code 检查。如果我可以使用
parameterize
,我可以使用一个函数并检查用户/预期返回代码。
如何利用我的固定装置作为
pytest.mark.parameterize
装饰器的一部分?
要将灯具作为参数化参数传递,请将它们作为字符串引用并使用关键字参数
indirect=['arg_name', ...]
。
然后使用参数名称
credentials
和 return_code
作为测试函数的参数。
@pytest.mark.parametrize(
"credentials, return_code",
[
("user1", 200),
("user2", 200),
("user3", 401),
],
# Only the `credentials` parameter is a reference to the
# fixture. The `return_code` should be passed as is.
indirect=["credentials"],
)
def test_login(credentials, return_code):
return True
可以在这里找到 pytest 的
parametrize
魔法与 indirect
的文档:
https://docs.pytest.org/en/stable/example/parametrize.html#apply-indirect-on-pspecial-arguments
据我所知,你不能。有一些解决方法:
def test_login():
for user, return_code in [
(User("user1", ...), 200),
(User("user2", ...), 200),
(User("user3", ...), 401),
]:
# test user
pass
或
def test_login(user1, user2, user3):
for user, return_code in [
(user1, 200),
(user2, 200),
(user3, 401),
]:
# test user
pass
优点:简单
缺点:如果第一个测试失败,后面的测试就不会运行。错误消息也没有显示哪个测试用例失败了。
@pytest.mark.parametrize('idx', [0,1,2])
def test_login(idx):
user, return_code = [
(User("user1", ...), 200),
(User("user2", ...), 200),
(User("user3", ...), 401),
][idx]
# test user
pass
(或使用
user1
、user2
、user3
灯具的等效物)
Pro:pytest 可以识别单独的测试
缺点:无用的索引/名称
@pytest.mark.fixture
def user_and_code(request):
if request.param == "user1":
return User("user1", ...), 200
elif request.param == "user2":
return User("user2", ...), 200
elif request.param == "user3":
return User("user3", ...), 401
@pytest.mark.parametrize('user_and_code', ['user1', 'user2', 'user3'], indirect=True)
def test_login(user_and_code):
user, return_code = user_and_code
# test user
pass
或者
@pytest.mark.fixture
def user_and_code(request, user1, user2, user3):
return [
(user1, 200),
(user2, 200),
(user3, 401),
][request.param]
@pytest.mark.parametrize('user_and_code', [0,1,2], indirect=True)
def test_login(user_and_code):
user, return_code = user_and_code
# test user
pass
Pro:测试代码干净,夹具可以在其他测试中重用
缺点:更复杂
根据文档,间接参数化是为了在测试收集期间跳过昂贵的设置,而不是让夹具可用于参数,正如您所看到的,这效果不太好。
上述每个选项中都有一些可以混合和匹配的选择,例如实例化所有用户或使用 if/else 仅实例化您需要的内容,使用
user1
等固定装置或创建内联用户、使用名称或索引等。这些示例仅显示一些替代方案。
pytest-cases 库 允许您对装置进行参数化。一个有效的例子:
import pytest_cases
@pytest_cases.fixture()
def one():
return 1
@pytest_cases.fixture()
def two():
return 2
@pytest_cases.parametrize("number", (one, two))
def test_it(number):
assert number in [1, 2]
您可能会遇到两种情况:
有了这个,您只需将固定装置作为测试函数中的最后一个参数传递即可:
import pytest
@pytest.fixture
def myfixture():
return 'world'
@pytest.mark.parametrize('hello', ['Hello', 'HELLO'])
def test_world(hello, myfixture):
output = hello.lower() + myfixture
assert output == 'helloworld'
通过
request
夹具并使用 getfixturevalue
: 查找您想要的夹具
import pytest
@pytest.fixture
def a():
return 'a'
@pytest.fixture
def b():
return 'b'
@pytest.mark.parametrize('fixture_name', ['a', 'b'])
def test_fixture_lookup(fixture_name, request):
output = request.getfixturevalue(fixture_name)
assert fixture_name == output