基于命令行开关进行 pytest 夹具参数化的更简洁方法?
Cleaner way to do pytest fixture parameterization based on command-line switch?
技术上我已经解决了我正在处理的问题,但我不禁觉得我的解决方案很丑陋:
我有一个 pytest 套件,我可以在两种模式下 运行:本地模式(用于开发测试;通过 Chrome 我的开发箱上的所有东西都只是 运行)和 Seriousface 回归测试模式(针对 CI;该套件在无数浏览器和操作系统上获得 运行)。我有一个命令行标志可以在两种模式之间切换,--test-local
。如果它在那里,我 运行 在本地模式。如果它不存在,我 运行 处于严肃的模式。以下是我的做法:
# contents of conftest.py
import pytest
def pytest_addoption(parser):
parser.addoption("--test-local", action="store_true", default=False, help="run locally instead of in seriousface mode")
def pytest_generate_tests(metafunc):
if "dummy" in metafunc.fixturenames:
if metafunc.config.getoption("--test-local"):
driverParams = [(True, None)]
else:
driverParams = [(False, "seriousface setting 1"), (False, "seriousface setting 2")]
metafunc.parameterize("dummy", driverParams)
@pytest.fixture(scope="function")
def driver(dummy):
_driver = makeDriverStuff(dummy[0], dummy[1])
yield _driver
_driver.cleanup()
@pytest.fixture
def dummy():
pass
问题是,dummy
夹具很丑。我试过让 pytest_generate_tests
直接参数化 driver
fixture,但最终 replacing fixture 而不是仅仅将东西输入其中,所以 cleanup()
测试完成时永远不会被调用。使用虚拟对象可以让我用我的参数元组替换虚拟对象,以便将其传递到 driver()
.
但是,重申一下,我所拥有的确实有效,感觉就像是一个简陋的 hack。
您可以尝试一种不同的方法:不是动态选择测试参数集,而是在其上声明所有参数集,但在启动时取消选择不相关的参数集。
# r.py
import pytest
real = pytest.mark.real
mock = pytest.mark.mock
@pytest.mark.parametrize('a, b', [
real((True, 'serious 1')),
real((True, 'serious 2')),
mock((False, 'fancy mock')),
(None, 'always unmarked!'),
])
def test_me(a, b):
print([a, b])
然后运行如下:
pytest -ra -v -s r.py -m real # strictly marked sets
pytest -ra -v -s r.py -m mock # strictly marked sets
pytest -ra -v -s r.py -m 'not real' # incl. non-marked sets
pytest -ra -v -s r.py -m 'not mock' # incl. non-marked sets
此外,skipif
标记可用于所选参数集(与取消选择不同)。它们由 pytest 原生支持。但是语法很丑陋。在 pytest 的 Skip/xfail with parametrize 部分中查看更多内容。
官方手册中也包含与Custom marker and command line option to control test runs中您的问题完全相同的案例。但是,它也不像 -m
测试取消选择那么优雅,并且更适合复杂的 运行 时间条件,而不是在测试的先验已知结构上。
技术上我已经解决了我正在处理的问题,但我不禁觉得我的解决方案很丑陋:
我有一个 pytest 套件,我可以在两种模式下 运行:本地模式(用于开发测试;通过 Chrome 我的开发箱上的所有东西都只是 运行)和 Seriousface 回归测试模式(针对 CI;该套件在无数浏览器和操作系统上获得 运行)。我有一个命令行标志可以在两种模式之间切换,--test-local
。如果它在那里,我 运行 在本地模式。如果它不存在,我 运行 处于严肃的模式。以下是我的做法:
# contents of conftest.py
import pytest
def pytest_addoption(parser):
parser.addoption("--test-local", action="store_true", default=False, help="run locally instead of in seriousface mode")
def pytest_generate_tests(metafunc):
if "dummy" in metafunc.fixturenames:
if metafunc.config.getoption("--test-local"):
driverParams = [(True, None)]
else:
driverParams = [(False, "seriousface setting 1"), (False, "seriousface setting 2")]
metafunc.parameterize("dummy", driverParams)
@pytest.fixture(scope="function")
def driver(dummy):
_driver = makeDriverStuff(dummy[0], dummy[1])
yield _driver
_driver.cleanup()
@pytest.fixture
def dummy():
pass
问题是,dummy
夹具很丑。我试过让 pytest_generate_tests
直接参数化 driver
fixture,但最终 replacing fixture 而不是仅仅将东西输入其中,所以 cleanup()
测试完成时永远不会被调用。使用虚拟对象可以让我用我的参数元组替换虚拟对象,以便将其传递到 driver()
.
但是,重申一下,我所拥有的确实有效,感觉就像是一个简陋的 hack。
您可以尝试一种不同的方法:不是动态选择测试参数集,而是在其上声明所有参数集,但在启动时取消选择不相关的参数集。
# r.py
import pytest
real = pytest.mark.real
mock = pytest.mark.mock
@pytest.mark.parametrize('a, b', [
real((True, 'serious 1')),
real((True, 'serious 2')),
mock((False, 'fancy mock')),
(None, 'always unmarked!'),
])
def test_me(a, b):
print([a, b])
然后运行如下:
pytest -ra -v -s r.py -m real # strictly marked sets
pytest -ra -v -s r.py -m mock # strictly marked sets
pytest -ra -v -s r.py -m 'not real' # incl. non-marked sets
pytest -ra -v -s r.py -m 'not mock' # incl. non-marked sets
此外,skipif
标记可用于所选参数集(与取消选择不同)。它们由 pytest 原生支持。但是语法很丑陋。在 pytest 的 Skip/xfail with parametrize 部分中查看更多内容。
官方手册中也包含与Custom marker and command line option to control test runs中您的问题完全相同的案例。但是,它也不像 -m
测试取消选择那么优雅,并且更适合复杂的 运行 时间条件,而不是在测试的先验已知结构上。