user3177338
user3177338

Reputation: 99

How to set dynamic default parameters for py.test?

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?

Upvotes: 7

Views: 5010

Answers (5)

hoefling
hoefling

Reputation: 66501

Note

The part of my answer with the custom plugin is way too overengineered; it is already sufficient to just reassign the xmlpath option as in this answer. Use that instead. The XML plugin reconfiguration is only needed if you want to make deeper changes in XML report assembly, which isn’t the case here.

Original answer

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.

Upvotes: 6

Evandro Coan
Evandro Coan

Reputation: 9475

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

Upvotes: 0

Drew Nutter
Drew Nutter

Reputation: 1135

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.

Upvotes: 7

Sameer Girolkar
Sameer Girolkar

Reputation: 127

@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)

Upvotes: 0

Anup
Anup

Reputation: 200

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

Upvotes: 0

Related Questions