pytest 参数化会话装置执行次数过多
pytest parameterized session fixtures execute too many times
考虑以下测试代码,它将模拟 运行 结果与预期结果进行比较。 运行 结果的值取决于参数化夹具 paramfixture 的值,它提供两个值,因此 运行 结果有两种可能的变体。由于它们都是会话装置,我们应该期望 run_result 装置只执行两次。
现在,请看一下测试用例test_run_result,它接收run_result和expected_result夹具进行比较,还接收公差夹具,这是参数化的有两个值。测试用例检查预期和结果之间的差异是否在容差范围内。请注意,运行 不依赖于公差。
出于某种原因,我不明白 Pytest 执行了三次 run_result() fixture。你能解释为什么吗?
这是使用 pytest vers 测试的。 2.9.1
顺便说一下,如果测试用例没有参数化或者使用装饰器而不是固定装置进行参数化,run_result固定装置将只执行两次,即:@pytest.mark.parametrize('tolerance', [1e-8, 1e-11]).
import pytest
runcounter = 0
@pytest.fixture(scope="session", params=[1e-8, 1e-11])
def tolerance(request):
"""Precision in floating point compare."""
return request.param
@pytest.fixture(scope='session', params=[1, 2])
def paramfixture(request):
return request.param
@pytest.fixture(scope="session")
def expected_result(paramfixture):
return 1 + paramfixture
@pytest.fixture(scope='session')
def run_result(paramfixture):
global runcounter
runcounter = runcounter + 1
print "Run #", runcounter, 'param:', paramfixture
return 1 + paramfixture
def test_run_result(run_result, expected_result, tolerance):
print "run_result: %d, expected_result: %d" % (run_result, expected_result)
assert abs(run_result - expected_result) < tolerance
Pytest 截图:
$ py.test -vs test/end2end/test_temp.py
===================================================== test session starts ======================================================
platform linux2 -- Python 2.7.11, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 -- /home/f557010/.conda/envs/sfpdev/bin/python
cachedir: .cache
rootdir: /home/f557010/svndev/SFP, inifile: pytest.ini
collected 4 items
test/end2end/test_temp.py::test_run_result[1e-08-1] Run # 1 param: 1
run_result: 2, expected_result: 2
PASSED
test/end2end/test_temp.py::test_run_result[1e-08-2] Run # 2 param: 2
run_result: 3, expected_result: 3
PASSED
test/end2end/test_temp.py::test_run_result[1e-11-2]
run_result: 3, expected_result: 3
PASSED
test/end2end/test_temp.py::test_run_result[1e-11-1] Run # 3 param: 1
run_result: 2, expected_result: 2
PASSED
=================================================== 4 passed in 0.01 seconds ===================================================
似乎 运行 进行了 4 次测试,而不是 3 次,如果它进行的是全组合,这就有意义 运行。
- 运行#1:参数 1,公差 1
- 运行 #2:参数 2,公差 1
- 运行 #3:参数 1,公差 2
- 运行 #4:参数 2,公差 2
运行宁四次在我看来是给定测试定义的合理方法。
pytest 的参数化就是获取固定装置并在合理的生命周期内保持它。它不会缓存所有的输入->输出映射。这不是你在这里想的,但如果你认为固定装置是数据库连接或 tcp 连接(如示例中的 smtp)之类的东西,它是有意义的。
你仍然有一个体面的论点,即对 pytest 进行足够的自省和优化会让你受益(这里假设 run_result 非常昂贵并且你希望最小化 运行s) .
这里为什么要"the wrong thing"?如果仔细查看灯具,公差是 "first order" 或最接近的参数化灯具。
"works":
丑陋、难以理解的变化
@pytest.fixture(scope="session", params=[0.01, 0.0002])
def tol(request):
"""Precision in floating point compare."""
return request.param
@pytest.fixture(scope="session")
def tolerance(tol):
"""Precision in floating point compare."""
return tol
为什么这行得通?它将公差参数删除为与其他灯具上的参数相同的 "level"。有了这个,pytest 实际上只 运行 run_tests 两次。
============================================ test session starts ============================================
<snip>
collected 4 items
test_tolerance.py::test_run_result[1-0.01] Run # 1 param: 1
run_result: 2, expected_result: 2 tolerance: 0.010000
PASSED
test_tolerance.py::test_run_result[1-0.0002]
run_result: 2, expected_result: 2 tolerance: 0.000200
PASSED
test_tolerance.py::test_run_result[2-0.0002] Run # 2 param: 2
run_result: 3, expected_result: 3 tolerance: 0.000200
PASSED
test_tolerance.py::test_run_result[2-0.01]
run_result: 3, expected_result: 3 tolerance: 0.010000
PASSED
========================================= 4 passed in 0.01 seconds ==========================================
你应该使用那个代码吗?请尽量不要 因为它太难理解了,如果你使用这样的 hack,请大量评论以“自责”。
您问了 "why",这里的关键是公差和参数夹具的参数处于不同的嵌套级别,"closest" 是迭代最慢的一个。 fixtures 不在这里缓存,它们只是按逻辑顺序使用,最里面的迭代速度最快。
考虑以下测试代码,它将模拟 运行 结果与预期结果进行比较。 运行 结果的值取决于参数化夹具 paramfixture 的值,它提供两个值,因此 运行 结果有两种可能的变体。由于它们都是会话装置,我们应该期望 run_result 装置只执行两次。
现在,请看一下测试用例test_run_result,它接收run_result和expected_result夹具进行比较,还接收公差夹具,这是参数化的有两个值。测试用例检查预期和结果之间的差异是否在容差范围内。请注意,运行 不依赖于公差。
出于某种原因,我不明白 Pytest 执行了三次 run_result() fixture。你能解释为什么吗?
这是使用 pytest vers 测试的。 2.9.1
顺便说一下,如果测试用例没有参数化或者使用装饰器而不是固定装置进行参数化,run_result固定装置将只执行两次,即:@pytest.mark.parametrize('tolerance', [1e-8, 1e-11]).
import pytest
runcounter = 0
@pytest.fixture(scope="session", params=[1e-8, 1e-11])
def tolerance(request):
"""Precision in floating point compare."""
return request.param
@pytest.fixture(scope='session', params=[1, 2])
def paramfixture(request):
return request.param
@pytest.fixture(scope="session")
def expected_result(paramfixture):
return 1 + paramfixture
@pytest.fixture(scope='session')
def run_result(paramfixture):
global runcounter
runcounter = runcounter + 1
print "Run #", runcounter, 'param:', paramfixture
return 1 + paramfixture
def test_run_result(run_result, expected_result, tolerance):
print "run_result: %d, expected_result: %d" % (run_result, expected_result)
assert abs(run_result - expected_result) < tolerance
Pytest 截图:
$ py.test -vs test/end2end/test_temp.py
===================================================== test session starts ======================================================
platform linux2 -- Python 2.7.11, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 -- /home/f557010/.conda/envs/sfpdev/bin/python
cachedir: .cache
rootdir: /home/f557010/svndev/SFP, inifile: pytest.ini
collected 4 items
test/end2end/test_temp.py::test_run_result[1e-08-1] Run # 1 param: 1
run_result: 2, expected_result: 2
PASSED
test/end2end/test_temp.py::test_run_result[1e-08-2] Run # 2 param: 2
run_result: 3, expected_result: 3
PASSED
test/end2end/test_temp.py::test_run_result[1e-11-2]
run_result: 3, expected_result: 3
PASSED
test/end2end/test_temp.py::test_run_result[1e-11-1] Run # 3 param: 1
run_result: 2, expected_result: 2
PASSED
=================================================== 4 passed in 0.01 seconds ===================================================
似乎 运行 进行了 4 次测试,而不是 3 次,如果它进行的是全组合,这就有意义 运行。
- 运行#1:参数 1,公差 1
- 运行 #2:参数 2,公差 1
- 运行 #3:参数 1,公差 2
- 运行 #4:参数 2,公差 2
运行宁四次在我看来是给定测试定义的合理方法。
pytest 的参数化就是获取固定装置并在合理的生命周期内保持它。它不会缓存所有的输入->输出映射。这不是你在这里想的,但如果你认为固定装置是数据库连接或 tcp 连接(如示例中的 smtp)之类的东西,它是有意义的。
你仍然有一个体面的论点,即对 pytest 进行足够的自省和优化会让你受益(这里假设 run_result 非常昂贵并且你希望最小化 运行s) .
这里为什么要"the wrong thing"?如果仔细查看灯具,公差是 "first order" 或最接近的参数化灯具。
"works":
丑陋、难以理解的变化@pytest.fixture(scope="session", params=[0.01, 0.0002])
def tol(request):
"""Precision in floating point compare."""
return request.param
@pytest.fixture(scope="session")
def tolerance(tol):
"""Precision in floating point compare."""
return tol
为什么这行得通?它将公差参数删除为与其他灯具上的参数相同的 "level"。有了这个,pytest 实际上只 运行 run_tests 两次。
============================================ test session starts ============================================
<snip>
collected 4 items
test_tolerance.py::test_run_result[1-0.01] Run # 1 param: 1
run_result: 2, expected_result: 2 tolerance: 0.010000
PASSED
test_tolerance.py::test_run_result[1-0.0002]
run_result: 2, expected_result: 2 tolerance: 0.000200
PASSED
test_tolerance.py::test_run_result[2-0.0002] Run # 2 param: 2
run_result: 3, expected_result: 3 tolerance: 0.000200
PASSED
test_tolerance.py::test_run_result[2-0.01]
run_result: 3, expected_result: 3 tolerance: 0.010000
PASSED
========================================= 4 passed in 0.01 seconds ==========================================
你应该使用那个代码吗?请尽量不要 因为它太难理解了,如果你使用这样的 hack,请大量评论以“自责”。
您问了 "why",这里的关键是公差和参数夹具的参数处于不同的嵌套级别,"closest" 是迭代最慢的一个。 fixtures 不在这里缓存,它们只是按逻辑顺序使用,最里面的迭代速度最快。