Search code examples
pythontestingpytestsalt-project

Set a variable based on a py.test (testinfra) check output


I am trying to make a testinfra test file more portable, I'd like to use a single file to handle tests for either a prod / dev or test env. For this I need to get a value from the remote tested machine, which I get by :

def test_ACD_GRAIN(host):
    grain = host.salt("grains.item", "client_NAME")
    assert grain['client_NAME'] == "test"

I'd need to use this grain['client_NAME'] value in different part of the test file, therefore I'd like to store it in a variable.

Anyway to do this ?


Solution

  • There are a lot of ways to share state between tests. To name a few:

    Using a session-scoped fixture

    Define a fixture with a session scope where the value is calculated. It will executed before the first test that uses it runs and then will be cached for the whole test run:

    # conftest.py
    @pytest.fixture(scope='session')
    def grain():
        host = ...
        return host.salt("grains.item", "client_NAME")
    

    Just use the fixture as the input argument in tests to access the value:

    def test_ACD_GRAIN(grain):
        assert grain['client_NAME'] == "test"
    

    Using pytest namespace

    Define an autouse fixture with a session scope, so it is autoapplied once per session and stores the value in the pytest namespace.

    # conftest.py
    
    import pytest
    
    
    def pytest_namespace():
        return {'grain': None}
    
    
    @pytest.fixture(scope='session', autouse=True)
    def grain():
        host = ...
        pytest.grain = host.salt("grains.item", "client_NAME")
    

    It will be executed before the first test runs. In tests, just call pytest.grain to get the value:

    import pytest
    
    def test_ACD_GRAIN():
        grain = pytest.grain
        assert grain['client_NAME'] == "test"
    

    pytest cache: reuse values between test runs

    If the value does not change between test runs, you can even persist in on disk:

    @pytest.fixture
    def grain(request):
        grain = request.config.cache.get('grain', None)
        if not grain:
            host = ...
            grain = host.salt("grains.item", "client_NAME")
            request.config.cache.set('grain', grain)
        return grain
    

    Now the tests won't need to recalculate the value on different test runs unless you clear the cache on disk:

    $ pytest
    ...
    $ pytest --cache-show
    ...
    grain contains:
      'spam'
    

    Rerun the tests with the --cache-clear flag to delete the cache and force the value to be recalculated.