I have a project that has the following structure:
Project/
|
+-- src/
| |
| +-- proj/
| |
| +-- __init__.py
| +-- code.py
| +-- tests/
| |
| +-- __init__.py
| +-- conftest.py
| +-- test_code.py
+-- doc/
+-- pytest.ini
+-- setup.py
The importable package proj
lives under the src
directory. Tests are installed along with it, in the proj.tests
sub-package. The code, the tests themselves, and the installation all work fine. However, I have trouble passing options to the tests.
I have an option, --plots
, that is defined in conftest.py
(under Project/src/proj/tests/
), which creates a .plots/
folder in the root folder and places some graphical debugging there:
# conftest.py
def pytest_addoption(parser):
parser.addoption("--plots", action="store_true", default=False)
There are two different ways I'd like to have to run this test:
The first is from the command line, in the Project
directory:
$ pytest --plots
This fails immediately with
usage: pytest [options] [file_or_dir] [file_or_dir] [...]
pytest: error: unrecognized arguments: --plots
inifile: /.../Project/pytest.ini
rootdir: /.../Project
If I add the package directory, the run goes fine:
$ pytest src/proj --plots
...
rootdir: /.../Project, inifile: pytest.ini
...
It also goes well if I specify the tests sub-directory:
$ pytest src/proj/tests --plots
...
rootdir: /.../Project, inifile: pytest.ini
...
The second way is to run the test
function of the package itself. The test
function is defined in Project/src/proj/__init__.py
like this:
# __init__.py
def test(*args, **kwargs):
from pytest import main
cmd = ['--pyargs', 'proj.tests']
cmd.extend(args)
return main(cmd, **kwargs)
The function is intended to be run after Project
is installed like this:
>>> import proj
>>> proj.test()
Generally, this works OK, but if I do
>>> proj.test('--plots')
I get the same error as usual:
usage: [options] [file_or_dir] [file_or_dir] [...]
: error: unrecognized arguments: --plots
inifile: None
rootdir: /some/other/folder
For this test, I ran python setup.py develop
and then cd
'dto
/some/other/folder` to make sure everything installed correctly.
My goal is to have both options working correctly when I pass in the --plots
command line option. It seems that I have found a workaround for option #1, which is to manually pass in one of the packages to pytest
, but I don't even fully understand how that works (why can I pass in either src/proj
or src/proj/tests
?).
So the question is, how to I get pytest to consistently run my test package so that the correct conftest.py
gets picked up? I am willing to consider just about any reasonable alternative that allows me to get the --plots
option working consistently in both the source (running in a shell from Project
) version and the proj.test()
version.
For reference, here is my pytest.ini
file:
# pytest.ini
[pytest]
testpaths = src/proj/tests
confcutdir = src/proj/tests
It doesn't appear to make any difference.
I am running with pytest 3.8.0, on Pythons 2.7, 3.3, 3.4, 3.5, 3.6 and 3.7 (in anaconda). In all versions, the results are reproducible.
You can put fixtures into individual test files, but to share fixtures among multiple test files, you need to use a conftest.py file somewhere centrally located for all of the tests. For the Tasks project, all of the fixtures will be in tasks_proj/tests/conftest.py.
You can have multiple nested directories/packages containing your tests, and each directory can have its own conftest.py with its own fixtures, adding on to the ones provided by the conftest.py files in parent directories.
The conftest.py file serves as a means of providing fixtures for an entire directory. Fixtures defined in a conftest.py can be used by any test in that package without needing to import them (pytest will automatically discover them).
conftest.py is where you setup test configurations and store the testcases that are used by test functions. The configurations and the testcases are called fixture in pytest. The test_*. py files are where the actual test functions reside.
Quote from the pytest_addoption
docs:
Note: This function should be implemented only in plugins or
conftest.py
files situated at the tests root directory due to howpytest
discovers plugins during startup....
Note that
pytest
does not findconftest.py
files in deeper nested sub directories at tool startup. It is usually a good idea to keep yourconftest.py
file in the top level test or project root directory.
When you call pytest
from the Project
dir, pytest
recognizes it as the root dir, so the addoption
hook in Project/src/proj/tests/conftest.py
is ignored.
If the project structure you described is a requirement, I'd move the pytest_addopts
hook impl to a separate plugin module; this can reside anywhere in the project dir, for example Project/src/proj/tests/plugin_plots.py
. Now, if you call
$ pytest -p src.proj.tests.plugin_plots
the addoption
hook impl will be executed and the args will be added correctly.
(you may need to adjust the sys.path
to be able to load the custom plugin module correctly, e.g. use PYTHONPATH=. pytest -p
or python -m pytest -p
, or install the project in editable mode etc)
To not to enter the -p
arg each time, persist it in pytest.ini
, e.g.
# pytest.ini
[pytest]
addopts=-p src.proj.tests.plugin_plots
When running the tests programmatically, ehnance the args list:
def test(*args, **kwargs):
from pytest import main
cmd = ['-p', 'proj.tests.plugin_plots', '--pyargs', 'proj.tests']
cmd.extend(args)
return main(cmd, **kwargs)
should work just fine.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With