How to set dynamic default parameters for py.test?
Question:
I have a framework which working under py.test. py.test can generate beauty reports with params –html and –junitxml. But clients that using my framework not always type this params to command line where they using py.test. I want make py.test to generate reports always when the py.test used with my framework. And i want to put this reports with log folder. So i need to generate path for report in runtime. Can i do this by fixtures? Or maybe by the plugin API?
Answers:
Configure pytest.ini file with parameters:
# content of pytest.ini
[pytest]
addopts = --html=report.html --self-contained-html
;addopts = -vv -rw --html=./results/report.html --self-contained-html
First of all, if you want to implicitly add the command line args to pytest
, you can use the pytest.ini
placed in the tests root dir with the addopts
config value:
[pytest]
addopts=--verbose --junit-xml=/tmp/myreport.xml # etc
Of course, if you want to dynamically calculate the directory to store the reports, then you can’t put it in the config and will need to extend pytest
. The best spot would be the pytest_configure
hook. Example:
# conftest.py
import tempfile
import pytest
from _pytest.junitxml import LogXML
@pytest.hookimpl(tryfirst=True)
def pytest_configure(config):
if config.option.xmlpath: # was passed via config or command line
return # let pytest handle it
if not hasattr(config, 'slaveinput'):
with tempfile.NamedTemporaryFile(suffix='.xml') as tmpfile:
xmlpath = tmpfile.name
config._xml = LogXML(xmlpath, config.option.junitprefix, config.getini('junit_suite_name'))
config.pluginmanager.register(config._xml)
If you remove the first if
block, then pytest
will completely ignore --junit-xml
arg passed via command line or in addopts
value in config.
Example run:
$ pytest
=================================== test session starts ====================================
platform darwin -- Python 3.6.3, pytest-3.3.1, py-1.5.2, pluggy-0.6.0
rootdir: /Users/hoefling/projects/private/stackoverflow/so-48320357, inifile:
plugins: forked-0.2, asyncio-0.8.0, xdist-1.22.0, mock-1.6.3, hypothesis-3.44.4
collected 1 item
test_spam.py . [100%]
--- generated xml file: /var/folders/_y/2qk6029j4c7bwv0ddk3p96r00000gn/T/tmp1tleknm3.xml ---
================================ 1 passed in 0.01 seconds ==================================
The xml report is now put in a tempfile.
@hoefling’s answer worked perfectly for me in conftest.py. the code looks simpler there.
def pytest_configure(config):
if not config.option.xmlpath and not hasattr(config, 'slaveinput'):
xmlpath = "test_report_" + str(int(time.time())) + ".xml"
config._xml = LogXML(xmlpath, config.option.junitprefix, config.getini('junit_suite_name'))
config.pluginmanager.register(config._xml)
Putting this in conftest.py will suffice:
def pytest_configure(config):
if config.option.xmlpath is None:
config.option.xmlpath = get_custom_xml_path() # implement this
The accepted answer is probably a bit more complicated than necessary for most people for a few reasons:
- The decorator doesn’t help. It doesn’t matter when this executes.
- There is no need make a custom LogXML since you can just set the property here and it will be used.
- slaveinput is specific to a pytest plugin xdist. I don’t think there is any need to check for that, especially if you don’t use xdist.
Just to keep things more clear, pytest uses argparse
and the request.config.option
is a argparse.Namespace
object. Then, if you would like to simulate a command line option as pytest ... --docker-compose-remove-volumes
, you can directly attribute the option docker_compose_remove_volumes
to request.config.option
(because --docker-compose-remove-volumes
is converted to docker_compose_remove_volumes
by argparse module).
This examples inverts the default option for --docker-compose-remove-volumes
which is false. But allow you to enable it back by providing --keep-containers
option to pytest.
def pytest_addoption(parser):
parser.addoption("--keep-containers", action="store_true", default=False,
help="Keeps docker-compose on failure.")
@pytest.fixture(scope='session', autouse=True)
def load_env(request):
is_to_keep_container = request.config.getoption("--keep-containers")
if not is_to_keep_container:
request.config.option.docker_compose_remove_volumes = True
I have a framework which working under py.test. py.test can generate beauty reports with params –html and –junitxml. But clients that using my framework not always type this params to command line where they using py.test. I want make py.test to generate reports always when the py.test used with my framework. And i want to put this reports with log folder. So i need to generate path for report in runtime. Can i do this by fixtures? Or maybe by the plugin API?
Configure pytest.ini file with parameters:
# content of pytest.ini
[pytest]
addopts = --html=report.html --self-contained-html
;addopts = -vv -rw --html=./results/report.html --self-contained-html
First of all, if you want to implicitly add the command line args to pytest
, you can use the pytest.ini
placed in the tests root dir with the addopts
config value:
[pytest]
addopts=--verbose --junit-xml=/tmp/myreport.xml # etc
Of course, if you want to dynamically calculate the directory to store the reports, then you can’t put it in the config and will need to extend pytest
. The best spot would be the pytest_configure
hook. Example:
# conftest.py
import tempfile
import pytest
from _pytest.junitxml import LogXML
@pytest.hookimpl(tryfirst=True)
def pytest_configure(config):
if config.option.xmlpath: # was passed via config or command line
return # let pytest handle it
if not hasattr(config, 'slaveinput'):
with tempfile.NamedTemporaryFile(suffix='.xml') as tmpfile:
xmlpath = tmpfile.name
config._xml = LogXML(xmlpath, config.option.junitprefix, config.getini('junit_suite_name'))
config.pluginmanager.register(config._xml)
If you remove the first if
block, then pytest
will completely ignore --junit-xml
arg passed via command line or in addopts
value in config.
Example run:
$ pytest
=================================== test session starts ====================================
platform darwin -- Python 3.6.3, pytest-3.3.1, py-1.5.2, pluggy-0.6.0
rootdir: /Users/hoefling/projects/private/stackoverflow/so-48320357, inifile:
plugins: forked-0.2, asyncio-0.8.0, xdist-1.22.0, mock-1.6.3, hypothesis-3.44.4
collected 1 item
test_spam.py . [100%]
--- generated xml file: /var/folders/_y/2qk6029j4c7bwv0ddk3p96r00000gn/T/tmp1tleknm3.xml ---
================================ 1 passed in 0.01 seconds ==================================
The xml report is now put in a tempfile.
@hoefling’s answer worked perfectly for me in conftest.py. the code looks simpler there.
def pytest_configure(config):
if not config.option.xmlpath and not hasattr(config, 'slaveinput'):
xmlpath = "test_report_" + str(int(time.time())) + ".xml"
config._xml = LogXML(xmlpath, config.option.junitprefix, config.getini('junit_suite_name'))
config.pluginmanager.register(config._xml)
Putting this in conftest.py will suffice:
def pytest_configure(config):
if config.option.xmlpath is None:
config.option.xmlpath = get_custom_xml_path() # implement this
The accepted answer is probably a bit more complicated than necessary for most people for a few reasons:
- The decorator doesn’t help. It doesn’t matter when this executes.
- There is no need make a custom LogXML since you can just set the property here and it will be used.
- slaveinput is specific to a pytest plugin xdist. I don’t think there is any need to check for that, especially if you don’t use xdist.
Just to keep things more clear, pytest uses argparse
and the request.config.option
is a argparse.Namespace
object. Then, if you would like to simulate a command line option as pytest ... --docker-compose-remove-volumes
, you can directly attribute the option docker_compose_remove_volumes
to request.config.option
(because --docker-compose-remove-volumes
is converted to docker_compose_remove_volumes
by argparse module).
This examples inverts the default option for --docker-compose-remove-volumes
which is false. But allow you to enable it back by providing --keep-containers
option to pytest.
def pytest_addoption(parser):
parser.addoption("--keep-containers", action="store_true", default=False,
help="Keeps docker-compose on failure.")
@pytest.fixture(scope='session', autouse=True)
def load_env(request):
is_to_keep_container = request.config.getoption("--keep-containers")
if not is_to_keep_container:
request.config.option.docker_compose_remove_volumes = True