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?

Asked By: user3177338

||

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
Answered By: Anup

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.

Answered By: hoefling

@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)
Answered By: Sameer Girolkar

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.
Answered By: Drew Nutter

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
Answered By: user
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.