Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pytest KeyError when attempting to access a command line variable

I've re-created the question with a new simpler fake-setup.

I have a framework that needs a command lane variable from pytest. This variable is called environment but when I attempt to access that variable I get a AttributeError: 'module' object has no attribute 'config'.

Here is my tests setup:

File organization

I know py.test loads in this order:

  1. Pytest plugins
  2. External Plugins
  3. conftest.py files in order from outer file to inner file.

I think I'm running into an issue where when I load the inner conftest.py I attempt to import the framework. When I import the framework it attempts to access the py.test variable. This variable, even though pytest has seen it in the pytest_addoption() sections of my outer-conftest.py, isn't ready to be used in pytest.

Contents of outer contest:

# content of conftest.py
import pytest

def pytest_addoption(parser):
    print("First")
    parser.addoption("--cmdopt", action="store", default="type1",
        help="my option: type1 or type2")


@pytest.fixture
def cmdopt(request):
    return request.config.getoption("cmdopt")

Contents of framework.py:

import pytest


class Environment:

    @staticmethod
    def env():
        '''Determine which environment we are operating in,
        if it fails - we assume dca
        '''
        return pytest.config.getoption('cmdopt')


class Users:
    __pool = Environment.env()

Contents of inner conftest.py:

import pytest
from testing.framework import Environment

Contents of test_sample.py:

# content of test_sample.py
def test_answer(cmdopt):
    if cmdopt == "type1":
        print ("first")
    elif cmdopt == "type2":
        print ("second")
    assert 0 # to see what was printed

I run the following command in the testing/ folder: py.test -q --cmdopt=type2

I receive the following error:

First
Second
Traceback (most recent call last):
  File "/usr/local/lib/python3.4/dist-packages/_pytest/config.py", line 513, in getconftestmodules
    return self._path2confmods[path]
KeyError: local('/home/damonp/Repos/stuff/<my-name-redacted>/testing/tests')

During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/usr/local/lib/python3.4/dist-packages/_pytest/config.py", line 537, in importconftest
    return self._conftestpath2mod[conftestpath]
KeyError: local('/home/damonp/Repos/stuff/<my-name-redacted>/testing/tests/conftest.py')

During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/usr/local/lib/python3.4/dist-packages/_pytest/config.py", line 543, in importconftest
    mod = conftestpath.pyimport()
  File "/usr/local/lib/python3.4/dist-packages/py/_path/local.py", line 641, in pyimport
    __import__(modname)
  File "/home/damonp/Repos/stuff/<my-name-redacted>/testing/tests/conftest.py", line 5, in <module>
    from testing.framework import Environment
  File "/home/damonp/Repos/stuff/<my-name-redacted>/testing/framework.py", line 14, in <module>
    class Users:
  File "/home/damonp/Repos/stuff/<my-name-redacted>/testing/framework.py", line 15, in Users
    __pool = Environment.env()
  File "/home/damonp/Repos/stuff/<my-name-redacted>/testing/framework.py", line 11, in env
    return pytest.config.getoption('cmdopt')
AttributeError: 'module' object has no attribute 'config'
ERROR: could not load /home/damonp/Repos/stuff/<my-name-redacted>/testing/tests/conftest.py

Is there a good way to use an external framework that relies on a pytest command line variable?

like image 857
xcen27 Avatar asked Oct 07 '15 14:10

xcen27


2 Answers

Ideally you would have fixtures that give you objects from your framework, for example a User(s) instance.

These fixtures can receive the cmdopt fixture, and could then pass that value to an init method or factory function.

e.g.

@pytest.fixture(scope="function")
def user(cmdarg):
    return Users(cmdarg, ...)

And then in your test,

def test_something(user):
    # do something with user object
like image 178
pfctdayelise Avatar answered Sep 27 '22 21:09

pfctdayelise


You can just add variable to module framework.py, import framework module to outer conftest.py and set this variable under cmdopt method.

Code in framework.py:

import pytest

cmdopt = None

class Environment:

    @staticmethod
    def env():
        '''Determine which environment we are operating in,
        if it fails - we assume dca
        '''
        return pytest.config.getoption('cmdopt')


class Users:
    __pool = Environment.env()

Code in outer conftest.py:

# content of conftest.py
import pytest
from . import framework

def pytest_addoption(parser):
    print("First")
    parser.addoption("--cmdopt", action="store", default="type1",
        help="my option: type1 or type2")


@pytest.fixture
def cmdopt(request):
    framework.cmdopt = request.config.getoption("cmdopt")
    return framework.cmdopt

The second thing you can do is to add variable to Environment class and operate on it. If you need some more logic while getting or setting this variable you can always use @property and setter inside Environment class.

like image 21
mikus420 Avatar answered Sep 27 '22 21:09

mikus420