[英]What is the best way to parameterize a pytest function using a fixture that returns a list?
现在我有一个测试文件,看起来像这个人为的例子:
import pytest
def colors():
# Expensive operation
return ['red', 'yellow', 'blue']
@pytest.mark.parametrize('color', colors())
def test_colors(color):
assert color != 'mauve'
这可以正常工作,但是由于colors()
是一项昂贵的操作,因此我想利用pytest的缓存并使其成为会话范围的固定装置。 此外,我还想编写其他测试,例如将其用作固定装置
def test_colors_list(colors):
assert len(colors) == 3
理想情况下,我的测试文件应类似于
@pytest.fixture(scope='session')
def colors():
# Expensive operation
return ['red', 'yellow', 'blue']
@pytest.mark.parametrize('color', colors)
def test_colors(color):
assert color != 'mauve'
def test_colors_list(colors):
assert len(colors) == 3
但这会导致错误,因此我无法正确处理。
理想情况下,我还想在colors()
引用其他灯具,同时还要对test_colors()
进行参数test_colors()
以生成多个函数。
我应该编写这些测试的最佳方法是什么?
一种解决方案是在单独的函数中生成颜色,并将其结果用作灯具的params
参数,然后可以多次使用
import pytest
def colors():
print('expensive')
return ['blue', 'red', 'mauve']
@pytest.fixture(params=colors())
def color(request):
return request.param
def test_bla(color):
print(color, end='')
def test_foo(color):
print(color, end='')
如果运行pytest -s
您将在输出中仅看到一次expensive
的字符串:
$py.test -sv
======================== test session starts =========================
platform linux -- Python 3.4.3, pytest-3.4.1, py-1.5.2, pluggy-0.6.0 -- /home/nils/test/bin/python3
cachedir: .pytest_cache
rootdir: /home/nils/test/bla, inifile:
collecting 0 items expensive
collected 6 items
test_bla.py::test_bla[blue] bluePASSED
test_bla.py::test_bla[red] redPASSED
test_bla.py::test_bla[mauve] mauvePASSED
test_bla.py::test_foo[blue] bluePASSED
test_bla.py::test_foo[red] redPASSED
test_bla.py::test_foo[mauve] mauvePASSED
====================== 6 passed in 0.04 seconds ======================
但是,昂贵的功能在导入时运行,这不是一个好习惯。
作为对colors()
结果的简单缓存的一项工作,它适用于会话范围:
@pytest.fixture(scope='session')
def colors():
try:
return colors._res
except AttributeError:
# Expensive operation
print()
print('expensive')
colors._res = ['red', 'yellow', 'blue']
return colors._res
@pytest.mark.parametrize('color', colors())
def test_colors(color):
assert color != 'mauve'
def test_colors_list(colors):
assert len(colors) == 3
虽然函数colors()
在其中被调用了两次,但是对于所有测试而言,昂贵的计算仅执行一次:
$ pytest -sv test_colors.py
================================================= test session starts ==================================================
platform darwin -- Python 3.6.4, pytest-3.0.7, py-1.4.33, pluggy-0.4.0 -- /Users/mike/miniconda3/envs/py36/bin/python
cachedir: .cache
rootdir: /Users/mike/tmp, inifile:
plugins: click-0.1, cov-2.4.0, mock-1.6.0, pylint-0.7.1, xdist-1.15.0, xonsh-0.5.8
collecting 0 items
expensive
collected 4 items
test_colors.py::test_colors[red] PASSED
test_colors.py::test_colors[yellow] PASSED
test_colors.py::test_colors[blue] PASSED
test_colors.py::test_colors_list PASSED
另一种解决方案是在会话固定装置中生成颜色,并在常规固定装置中迭代返回的结果,然后可以多次使用
import pytest
@pytest.fixture(scope='session')
def a():
return 'purple'
@pytest.fixture(scope='session')
def colors(a):
print('expensive')
return ['blue', 'red', 'mauve', a]
@pytest.fixture(params=range(4)) # unfortunately we need to know the number of values returned by `colors()`
def color(request, colors):
return colors[request.param]
def test_bla(color):
print(color, end='')
def test_foo(color):
print(color, end='')
如果运行pytest -s
您将在输出中仅看到一次expensive
的字符串:
$ py.test -sv
======================== test session starts =========================
platform linux -- Python 3.4.3, pytest-3.4.1, py-1.5.2, pluggy-0.6.0 -- cwd
cachedir: .pytest_cache
rootdir: cwd, inifile:
collected 8 items
test_bla.py::test_bla[0] expensive
bluePASSED
test_bla.py::test_bla[1] redPASSED
test_bla.py::test_bla[2] mauvePASSED
test_bla.py::test_bla[3] purplePASSED
test_bla.py::test_foo[0] bluePASSED
test_bla.py::test_foo[1] redPASSED
test_bla.py::test_foo[2] mauvePASSED
test_bla.py::test_foo[3] purplePASSED
====================== 8 passed in 0.03 seconds ======================
另外:在导入时间上不会调用昂贵的函数(请查看输出中出现了expensive
的位置),并且您可以使用其他具有colors
会话固定装置
语法错误可以通过以下方式解决:
test_1.py:
import pytest
@pytest.fixture(scope='session')
def colors():
# Expensive operation
return ['red', 'yellow', 'blue']
@pytest.mark.parametrize('color',colors())
def test_colors(color):
assert color != 'mauve'
def test_colors_list(colors):
assert len(colors) == 3
但是,使用--setup-show
选项在pytest中运行先前的代码,其中显示了灯具设置和拆卸的详细信息,将显示以下内容:
test_1.py::test_colors[red]
SETUP F color[red]
test_1.py::test_colors[red] (fixtures used: color)PASSED
TEARDOWN F color[red]
test_1.py::test_colors[yellow]
SETUP F color[yellow]
test_1.py::test_colors[yellow] (fixtures used: color)PASSED
TEARDOWN F color[yellow]
test_1.py::test_colors[blue]
SETUP F color[blue]
test_1.py::test_colors[blue] (fixtures used: color)PASSED
TEARDOWN F color[blue]
test_1.py::test_colors_list
SETUP S colors
test_1.py::test_colors_list (fixtures used: colors)PASSED
TEARDOWN S colors
从输出中可以看到,每个测试用例的颜色设置都已设置/拆卸,最后一个测试用例的颜色设置都已再次设置/拆卸。
为避免这种情况,您可以最少地重构代码,如下所示:
test_1.py:
@pytest.fixture(scope='session')
def colors():
# Expensive operation
return ['red', 'yellow', 'blue']
def test_colors(colors):
for color in colors:
assert color != 'mauve'
def test_colors_list(colors):
assert len(colors) == 3
使用--setup-show
再次运行pytest将输出以下内容:
test_1.py::test_colors
SETUP S colors
test_1.py::test_colors (fixtures used: colors)PASSED
test_1.py::test_colors_list
test_1.py::test_colors_list (fixtures used: colors)PASSED
TEARDOWN S colors
您可以在其中检查所有测试的夹具是否仅设置/拆卸一次。
您可以看一下pytest文档中的相关部分
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.