python nose don't change working directory path - python

I am having an issue with nose.
I want to run tests in a tests directory but I don't want nose to change the working directory. This is because I am using relative imports in my library and the unittests themselves.
First the directory structure:
app_dir
app.py
library_dir
tests_dir
test_1.py
test_2.py
I am currently running my tests like this from within the app_dir directory:
python -m unittest discover -s library_dir.tests_dir
This works fine.
Now, I would like to do this with nose (nosetests).
When I try it, I get the following:
nosetests library_dir.tests_dir --collect-only -vv
Failure: SystemError (Parent module '' not loaded, cannot perform relative import) ... ok
Failure: SystemError (Parent module '' not loaded, cannot perform relative import) ... ok
I've looked at similar questions on SO already (there are many) and one suggestion for this issue that I can see is to remove the relative imports from within the unittest itself but I like this pattern. Additionally, if I do change to an absolute import, I get an
(No module named '<the_module_under_test')
Is there any way for nose to find tests from a directory (like what the -w option provides BUT keeping the working directory the same as that from the terminal which invoked nose?

Whoops, looks like all I needed to do was to add __init__.py to the library and the subdirectory tests directory. I was working off of the assumption nose would work as expected with PEP 420
Blockquote
PEP 420: Implicit Namespace Packages
Native support for package directories that don’t require init.py marker files and can automatically span multiple path segments (inspired by various third party approaches to namespace packages, as described in PEP 420)

Related

Python adding unit test inside package

I have a python package for which I am trying to write unit tests
The package looks as below
helper/
utils/
app/
requirements.txt
README.md
tests/
I come from java background so I thought of organizing the tests in the same package as that of their source therefore my tests directory looks as below
tests/
helper/
helper_a_test.py
utils/
util_a_test.py
app/
myapp_test.py
when I trying invoking the tests as below
python -m unittest discover
The test fails due to import error from source with error module app, helper, utils not found.
I have __init__.py file is all my packages.
I moved all the tests inside tests sub-directory into tests root directory as below.
tests/
helper_a_test.py
util_a_test.py
myapp_test.py
Now all test works as expected.
Can someone explain why is this happening, also is it good practice to keep all tests inside one directory rather that on its own package?
You have at least two ways to go back to your original structure and make it work:
The first and most direct one is adding an __init__.py in your test structure (at all levels, also the tests folder, that you may have missed).
The second one is to transform your code into a python package (adding a setup.py, so that your app will be installable with pip), install your package in the local interpreter and then run the tests.
I would also suggest using pytest and call directly pytest instead of python -m unittest.

Python: Nosetests with multiple files

This is a broad question because no one seems to have found a solution to it as yet so I think asking to see a working example might prove more useful. So here goes:
Has anyone run a nosetests on a python project using imports of multiple files/packages?
What I mean is, do you have a directory listing such as:
project/
|
|____app/
|___main.py
|___2ndFile.py
|___3rdFile.py
|____tests/
|____main_tests.py
Where your main.py imports multiple files and you perform a nosetests from the project file of utilizing a test script in the main_tests.py file? If so please can you screen shot your import section both of all your main files and your main_tests.py file?
This seems to be a major issue in nosetests, with no apparent solution:
Nosetests Import Error
A test running with nosetests fails with ImportError, but works with python command
https://github.com/nose-devs/nose/issues/978
https://github.com/nose-devs/nose/issues/964
You can't have python modules starting with a digit, so 2ndFile.py, 3rdFile.py won't actually work (rename them).
You'll need an __init__.py inside the app directory, for it to be considered a package, so add that (it can be empty file).
You don't need an __init__.py in the tests directory!
The import statements in main_tests.py should look like from app.main import blah
The absolute path of the project directory needs to be in your sys.path. To achieve this, set an environment variable: export PYTHONPATH=/path/to/project
Now running nosetests should work.

How to run tests without installing package?

I have some Python package and some tests. The files are layed out following http://pytest.org/latest/goodpractices.html#choosing-a-test-layout-import-rules
Putting tests into an extra directory outside your actual application
code, useful if you have many functional tests or for other reasons
want to keep tests separate from actual application code (often a good
idea):
setup.py # your distutils/setuptools Python package metadata
mypkg/
__init__.py
appmodule.py
tests/
test_app.py
My problem is, when I run the tests py.test, I get an error
ImportError: No module named 'mypkg'
I can solve this by installing the package python setup.py install but this means the tests run against the installed package, not the local one, which makes development very tedious. Whenever I make a change and want to run the tests, I need to reinstall, else I am testing the old code.
What can I do?
I know this question has been already closed, but a simple way I often use is to call pytest via python -m, from the root (the parent of the package).
$ python -m pytest tests
This works because -m option adds the current directory to the python path, and hence mypkg is detected as a local package (not as the installed).
See:
https://docs.pytest.org/en/latest/usage.html#calling-pytest-through-python-m-pytest
The normal approach for development is to use a virtualenv and use pip install -e . in the virtualenv (this is almost equivalent to python setup.py develop). Now your source directory is used as installed package on sys.path.
There are of course a bunch of other ways to get your package on sys.path for testing, see Ensuring py.test includes the application directory in sys.path for a question with a more complete answer for this exact same problem.
On my side, while developing, I prefer to run tests from the IDE (using a runner extension) rather than using the command line. However, before pushing my code or prior to a release, I like to use the command line.
Here is a way to deal with this issue, allowing you to run tests from both the test runner used by your IDE and the command line.
My setup:
IDE: Visual Studio Code
Testing: pytest
Extension (test runner): https://marketplace.visualstudio.com/items?itemName=LittleFoxTeam.vscode-python-test-adapter
Work directory structure (my solution should be easily adaptable to your context):
project_folder/
src/
mypkg/
__init__.py
appmodule.py
tests/
mypkg/
appmodule_test.py
pytest.ini <- Use so pytest can locate pkgs from ./src
.env <- Use so VsCode and its extention can locate pkgs from ./src
.env:
PYTHONPATH="${PYTHONPATH};./src;"
pytest.ini (tried with pytest 7.1.2):
[pytest]
pythonpath = . src
./src/mypkg/appmodule.py:
def i_hate_configuring_python():
return "Finally..."
./tests/mypkg/appmodule_test.py:
from mypkg import app_module
def test_demo():
print(app_module.i_hate_configuring_python())
This should do the trick
Import the package using from .. import mypkg. For this to work you will need to add (empty) __init__.py files to the tests directory and the containing directory. py.test should take care of the rest.

Why doesn't my current directory show up in the path using pytest on Windows?

I have the following folder structure;
myapp\
myapp\
__init__.py
tests\
test_myapp.py
and my pwd is
C:\Users\wwerner\programming\myapp\
I have the following test setup:
import sys
import pprint
def test_cool():
pprint.pprint(sys.path)
assert False
That produces the following paths:
['C:\\Users\\wwerner\\programming\\myapp\\tests',
'C:\\Users\\wwerner\\programming\\envs\\myapp\\Scripts',
'C:\\Windows\\system32\\python34.zip',
'C:\\Python34\\DLLs',
'C:\\Python34\\lib',
'C:\\Python34',
'C:\\Users\\wwerner\\programming\\envs\\myapp',
'C:\\Users\\wwerner\\programming\\envs\\myapp\\lib\\site-packages']
And when I try to import myapp I get the following error:
ImportError: No module named 'myapp'
So it looks like it's not adding the current directory to my path.
By changing my import line to look like this:
import sys
sys.path.insert(0, '.')
import myapp
I am then able to import myapp with no problems.
Why does my current directory not show up in the path when running pytest? Is my only workaround to insert . into the sys.path? (I'm using Python 3.4 if it matters)
Ahah!
After comparing the layout of my cookiecutter repo, it turns out to be way more simple (and better) than that.
tests/
__init__.py
test_myapp.py
A simple addition of the __init__.py file to my test dir allows me to run py.test from my main directory.
Using an installable package
If you have an installable package (setup.py or pyproject.toml file with a build-system defined) then you want to test against the installed package.
pip install --editable .
pytest
The simplest possible way to make the project shown in the question into an installable package would be by adding this setup.py:
from setuptools import setup
setup(
name="myapp",
version="0.1",
packages=["myapp"],
)
This will put the myapp code at /path/to/myapp/.venv/lib/python3.XY/site-packages, which is in the sys.path of the virtual environment. Now myapp can be imported from the site-packages dir, just as it would be for a user installation. It is neither necessary nor desirable for the current working directory to be present on sys.path during test execution.
Not using an installable package
The project shown in the question does not have any installer, so it can't be installed. It can still be tested by making sure the project root (i.e. the directory which contains both myapp and tests as subdirectories) is present on sys.path.
The best way to do this is to use python -m pytest, rather than invoking the bare pytest command. When you use python -m pytest it adds the current working directory to the start of sys.path. That's the normal Python behavior when executing a package as __main__ (documented here) and it's also a documented usage for pytest - see Invoking pytest versus python -m pytest.
Why does adding an __init__.py to the tests subdirectory (not) work?
The directory structure shown in the question is the "Tests outside application code" pattern, documented here. This is also the directory structure I recommend, since it creates a clear distinction between library/application code and test code.
It's not recommended to add __init__.py files inside the test directories when using a "Tests outside application code" structure, since the test files aren't intended to be "packaged" (e.g. test files do not really need to import from other test files, and they do not need to be installed at all for end users of your package).
The reason adding a myapp/__init__.py actually allows myapp to be imported by pytest, as described in Wayne's answer is actually an accident due to the way test discovery appends sys.path during the test collection phase. This is described as "problematic" in the docs
... this introduces a subtle problem: in order to load the test modules from the tests directory, pytest prepends the root of the repository to sys.path, which adds the side-effect that now mypkg is also importable
They go on to strongly recommend using the src-layout if you intend to have __init__.py files inside test directories, to avoid this confusion of the import system.
But perhaps the best reason not to rely on this side-effect is that pytest collection actually can work in multiple modes (see import modes), and Wayne's answer relies upon pytest using the default "prepend" mode. It is currently mentioned that a future version will switch to "importlib" mode as default:
We intend to make importlib the default in future releases.
The accepted answer does not work with pytest --import-mode=importlib and so will stop working altogether at some stage.
sys.path automatically has the script's directory in it, and not the current working directory.
I am guessing that your script in placed in tests directory. Based on this assumption, your code should look like this:
import sys
import os
ROOT_DIR = os.path.dirname(os.path.dirname(__file__))
sys.path.append(ROOT_DIR)
import myapp # Should work now
Use the environment variable PYTHONPATH.
In Windows:
set PYTHONPATH=.
py.test
In Unix:
PYTHONPATH=. py.test

Py.test No module named *

I have a folder structure like this
App
--App
--app.py
--Docs
--Tests
--test_app.py
In my test_app.py file, I have a line to import my app module. When I run py.test on the root folder, I get this error about no module named app. How should I configure this?
Working with Python 3 and getting the same error on a similar project layout, I solved it by adding an __init__ file to my tests module.
$ touch tests/__init__.py
I'm not great at packaging and importing, but I think that this helps pytest work out where the target App module is located.
I already had an __init__.py file in the /App/App directory and wanted to run tests from the project root without any path-mangling magic:
python -m pytest tests
The output immediately looks like this:
➟ python -m pytest tests
====================================== test session starts ======================================
platform linux -- Python 3.5.1, pytest-2.9.0, py-1.4.31, pluggy-0.3.1
rootdir: /home/andrew/code/app, inifile:
plugins: teamcity-messages-1.17
collected 46 items
... lines omitted ...
============================= 44 passed, 2 skipped in 1.61 seconds ==============================
I had a similar problem and had to delete __init__.py from the root and add an __init__.py to the tests folder.
So you are running py.test from /App. Are you sure /App/App is in your $PYTHONPATH?
If it's not, code that tries to import app will fail with such a message.
EDIT0: including the info from my comment below, for completeness.
An attempt to import app will only succeed if it was executed inside /App/App, which is not the case here. You probably want to make /App/App a package by putting __init__.py inside it, and change your import to qualify app as from App import app.
EDIT1: by request, adding further explanation from my second comment below.
By putting __init__.py inside /App/App, that directory becomes a package. Which means you can import from it, as long as it - the directory - is visible in the $PYTHONPATH. I.e. you can do from App import app if /App is in the $PYTHONPATH. Your current working directory gets automatically added to $PYTHONPATH, so when you run a script from /App, the import will work.
Running pytest with the python -m pytest command helps with this exact thing.
Since your current package is not yet in your $PYTHONPATH or sys.path - pytest gets this error.
By using python -m pytest you automatically add the working directory into sys.path for running pytest. Their documentation also mentions:
This is almost equivalent to invoking the command line script pytest
I also got same error while running test cases for my app located as below
myproject
--app1
--__init.py__
--test.py
--app2
--__init.py__
--test.py
--__init.py__
I deleted my myproject's init.py file to run my test cases.
TL;DR
You might as well add an empty conftest.py file to your root app folder.
(if you take a look at the question folder structure, that would be the same level as the "Tests" folder, not inside of it).
More info:
Pytest looks for conftest.py files inside all your project folders.
conftest.py provides configuration for the file tree pytest finds it in. Because pytest somehow scans all subdirectories starting from conftest.py folder, it should find packages/modules outside the tests folder (as long as a conftest.py file is in your app root folder).
Eventually, you might want to write some code in your empty conftest.py, specially to share fixtures among different tests files, in order to avoid duplicate code. Afterall, the DRY principle (Don't Repeat Yourself) should also be follwed when writing tests.
Adding __init.py__ to the tests folder also should help pytest to find modules throughout your application. However, note that Python 3.3+ has implicit namespace packages that allow it to create a packages without an __init__.py file. That been said, creating __init__.py files for this specific purpose seems more like a a workaround for pytest than a python requirement. More about that in: Is __init__.py not required for packages in Python 3.3+
I got the similar issue. And after trying multiple things including installing pytest on virtual environment and adding/removing __init__.py file from the package, none worked for me.
Solution that worked for me is(windows solution):
Added Project folder(not package folder) to python path(set PYTHONPATH=%PYTHONPATH%;%CD%)
Ran my script from Project Folder and boom, it worked.
I hit the same issue.
my-app
--conf
--my-app
--tests
I set the __init__.py files. I added a conftest.py ( for sharing pytest.fixtures ). I added this to my Poetry file ( pyproject.toml:
[tool.pytest.ini_options]
pythonpath = [
"."
Turned out it was my use of hyphens and not underscores ! Noo...
# pytest can't find Module
--my-app
# works
--my_app
What worked for me: I had to make absolute imports in my test file and call python -m test in the root folder.
This worked for me:
Went to parent app, and pip install -e . (install a local and editable app).

Categories