Add dir to Django search path for tests - python

I am using a single django for various custom distributions.
I arrived at a solution where the apps unique to each distribution live outside of the django projects folder (lets call it DIR/), and added this outside DIR/ to the Django path using
How to keep all my django applications in specific folder (adding to the sys.path solution)
The project runs, however now it does not discover tests. Before moving the apps out of the Django project folder I could simply run all tests with:
manage.py test
However, now the tests aren't found. In addition to adding DIR/ to settings.py, I tried adding it to manage.py and it did not help. By reading the django docs I discovered I can specify a module like this:
manage.py test app_name.tests
The above works, but is impractical for many apps. How can I add a path for where to search for tests?
I read this but it only describes the problem, not the solution:
Django test runner not finding tests
Request for my project structure:
somefolder/
|-- dist/
| |-- dist1/apps/
| | |---- app11/
| | '---- app12/
| |
| '-- dist2/apps/
| |---- app21/
| '---- app22/
|-- src/
|-- manage.py
|-- project/settings.py
|-- appA/
'-- appB/
Since asking have found out this works (not ideal):
manage.py test app11 app12 app21 app22 --keepdb

The key is to add some empty __init__py files in your dist, dist/dist1 and dist/dist1/apps directories.
I just tried putting together a toy project with the following structure:
.
├── dist
│   ├── dist1
│   │   ├── apps
│   │   │   ├── app_external
│   │   │   │   ├── admin.py
│   │   │   │   ├── apps.py
│   │   │   │   ├── __init__.py
│   │   │   │   ├── migrations
│   │   │   │   │   └── __init__.py
│   │   │   │   ├── models.py
│   │   │   │   ├── tests.py
│   │   │   │   └── views.py
│   │   │   └── __init__.py
│   │   └── __init__.py
│   └── __init__.py
└── src
├── app_internal
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── manage.py
└── project
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
Notice that there is an empty __init__.py file in each directory under dist/. This is what makes each directory a Python package and make the Python test machinery to look for tests inside those directories.
Also notice that I have an internal app called app_internal and an external one called app_external in a directory structure similar to yours.
Both the app_internal and app_external have one fake test in the tests.py file.
app_external/tests.py content:
from django.test import TestCase
class ExternalTestCase(TestCase):
def test_fake(self):
self.assertTrue(False)
app_internal/tests.py content:
from django.test import TestCase
class InternalTestCase(TestCase):
def test_fake(self):
self.assertTrue(True)
Here we expect that the app_external test will fail and the app_internal test will succeed.
I can call the app_internal test by giving the usual command:
$ ./manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Destroying test database for alias 'default'...
And I can call the app_external test by giving the following command:
$ ./manage.py test ..
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
F
======================================================================
FAIL: test_fake (dist.dist1.apps.app_external.tests.ExternalTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/augusto/develop/apps_outside/dist/dist1/apps/app_external/tests.py", line 10, in test_fake
self.assertTrue(False)
AssertionError: False is not true
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (failures=1)
Destroying test database for alias 'default'...
Notice how I passed .. as the parameter to the test command.
This point to the parent directory with respect to src/, so it finds the dist package and every other package inside it. I expect that this will find all tests under your dist/ directory.
I can also run all internal and external tests at once by giving the following command:
$ ./manage.py test . ..
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.F
======================================================================
FAIL: test_fake (dist.dist1.apps.app_external.tests.ExternalTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/augusto/develop/apps_outside/dist/dist1/apps/app_external/tests.py", line 10, in test_fake
self.assertTrue(False)
AssertionError: False is not true
----------------------------------------------------------------------
Ran 2 tests in 0.001s
FAILED (failures=1)
Destroying test database for alias 'default'...
I tested this using Django 1.11.23 and Python 2.7.12.

Related

Pytest Django Importing apps issues

I have a question regarding pytest and django 3.1. I have an app structure like that:
main_app
├── config
│   └── settings
│   └── base.py / local.py / prod.py
├── main_app
│   ├── sub_apps
│   │   └── models.py tests.py apps.py views.py etc...
│   ├── templates
│   │   ├── account
│   │   ├── sub_apps
│   │   ├── pages
│   │   ├── userpreferences
│   │   └── users
│   ├── userpreferences
│   │   └── models.py tests.py apps.py views.py etc...
│   ├── users
│   │   └── tests --> test_models.py test_views.py test_forms.py etc...
│   └── utils
└── requirements
Following the structure of pydanny/cookiecutter.
With this cookiecutter, the preferred test method is via pytest. But pytest is giving me a bunch of headaches:
In my settings, INSTALLED_APPS I register sub_apps within the
LOCAL_APPS =["sub_app_1", "sub_app_2", ...]
When I start django, everything is fine. But if I want to run tests, pytest is complaining heavily, it can't import the sub_app modules.
I suspect mixed up relative and absolute import paths but am not seeing how to solve this atm.
As a background: I use a separate userpreferences model which is imported in the settings via
LOCAL_APPS = ["main_app.userpreferences.apps.UserpreferencesConfig"]
In the apps.py I have to define the app name as
name = "main_app.userpreferences"
Otherwise I get an Runtime error stating the Model class does'nt declare an explicit app_label / is not installed in INSTALLED_APPS.
While django runs just fine with these imports, pytest exits before running tests with:
ModuleNotFoundError: No module named "sub_app_1"
I tried to change every import of modules in the sub_apps to e.g.:
from .sub_app_1 import models --> from main_app.sub_app_1 import models
but then django won't run anymore while the tests seem to run (pytest is starting and telling me 5/7 tests passed, while also occasionally printing import errors)
How do I resolve this?

Python ModuleNotFoundError: No module named 'xxx'

Pycharm imports works correctly, but when I execute test_settings.py in the terminal, I have this error:
ImportError while importing test module '/home/ed/PycharmProjects/TTime/tests/test_settings.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests/test_settings.py:3: in
from ttime import app, db
E ModuleNotFoundError: No module named 'ttime'
Here is my Flask project structure. I tried to include init.py in the main folder and in the tests folder, but nothing changes. It seems like somethings is wrong with python paths.
.
├── config.py
├── README.md
├── requirements.txt
├── runserver.py
├── tests
│   ├── functional
│   │   └── __init__.py
│   ├── pytest.ini
│   ├── test_settings.py
│   └── unit
│   ├── __init__.py
│   └── test_models.py
└── ttime
├── __init__.py
├── models.py
├── routes.py
├── static
└── templates
├── base.html
└── index.html

pytest get the file path of current test file

I can't seem to get the file path of the current file being tested in pytest.
For instance consider something like the following directory structure:
devops_scripts
├── devops_utilities
│   ├── backupMonthly.py
│   ├── generateAnsibleINI.py
│   └── tests
│   └── test_backup.txt
├── monitoring
│   ├── analyizeCatalinaLog.py
│   ├── fetchCatalinaLogs.py
│   └── getaccountcredit.py
├── pi_provisioning
│   ├── piImageMake.sh
│   ├── piImageRead.sh
│   └── piImageSquashSd.py
└── script_utilities
   ├── README.md
   ├── __init__.py
   ├── execute.py
   ├── path_handling.py
   ├── sql_handling.py
   └── tests
   ├── sourcedirfortests
   │   ├── file1.txt
   │   └── file2.txt
   ├── test_ansible.txt
   └── test_execute.txt
If I run pytest from the top level it will descend to testing test_execute.txt. When test_execute.txt is under testing how do I get the file path to it? (I can get the rootdir through os.path.abspath('.') but that is not what I need)
(I need to be able to set these paths in order to test some execution things on file1.txt and file2.txt. I also need this to work no matter how deeply nested the various things I am trying to test.) I am not so interested in setting up specific temp testing directories and taking them down, etc. I just need the path of the file which is being tested.
I have tried things like:
>>> print os.path.abspath(__file__)
But that just yields: UNEXPECTED EXCEPTION: NameError("name '__file__' is not defined",)
I am not above even accessing some internal functions / objects in py.test
(I should add that I need this to work in Python 2.7 and Python 3.x)
Pytest sets PYTEST_CURRENT_TEST env var for the current running test. Not only it has current file info, it has info of current collected test id too (like test name, params etc.).
import os
def test_current():
print(os.getenv('PYTEST_CURRENT_TEST'))
You must use -s flag for pytest run if you want to see the printed text.
From Reference Doc:
During the test session pytest will set PYTEST_CURRENT_TEST to the current test nodeid and the current stage, which can be setup, call and teardown.

Run Django tests with nosetests

My python apps testing is performed on the remote server with command nosetests. I cannot modify the way tests are started nor can I add options to it. I have Django app with tests, but tests are not working properly.
My project structure:
project
├── README.md
├── setup.py
├── mysite
│   ├── blog
│   │   ├── __init__.py
│   │   ├── models.py
│   │   ├── tests.py
| | ├── ...
│   ├── db.sqlite3
│   ├── manage.py
│   ├── mysite
│   │   ├── __init__.py
│   │   ├── settings.py
| | ├── ...
Command nosetests is executed in project directory. I want it to properly run tests.py which has 2 Django testcases. I tried creating tests directory in project root and invoke tests with DiscoverRunner):
os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'
test_dir = os.path.dirname(os.path.dirname(__file__)) # one level up
sys.path.insert(0, os.path.join(test_dir, 'mysite'))
class ServerTest(unittest.TestCase):
def test_runtests(self):
django.setup()
self.test_runner = DiscoverRunner(verbosity=1, interactive=True, failfast=False)
failures = self.test_runner.run_tests(['mysite'])
self.assertEqual(failures, 0)
It works but the problem is all the tests are considered as a single test and wrong reports are produced by the server.
Another solution: if I add empty __init__.py to project/mysite nose discovers tests.py but the tests fail because 'Apps are not loaded yet' which probably means I have to invoke django.setup() earlier but I don't know how to do it. I found a plugin for the nose which does it but I cannot install plugins or alter options on the remote machine.
Any ideas how to make any of my approaches solve the problem?
Firsts things first, you should install and configure django-nose if you haven't done so already:
pip install django-nose
Then add it on your INSTALLED_APPS:
INSTALLED_APPS = (
...
'django_nose'
)
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
Now for the nosetests command to run correctly, you can create a folder named tests with the following structure:
project
├── mysite
│ ├── blog
│ │ ├── __init__.py
│ │ ├── models.py
| | ├── ...
│ ├── mysite
│ │ ├── __init__.py
│ │ ├── settings.py
| | ├── ...
│ ├── tests
│ │ ├── __init__.py
│ │ ├── test_a_whole_class_of_methods.py
│ │ ├── test_a_whole_view.py
│ │ ├── test_one_of_my_methods.py
│ │ ├── test_another_one_of_my_methods.py
| | ├── ...
│ ├── db.sqlite3
│ ├── manage.py
├── README.md
├── setup.py
DON'T FORGET THE __init__.py FILE INSIDE THE tests FOLDER!!!
Now when you run nosetests from the root of your project, it will run every test in the tests folder:
~ $ cd path/to/project
path/to/project $ nosetests
Since you can add files to the root of your project, you could try adding a setup.cfg file there that nose will look for when executing, and in it specify where to look for tests:
# setup.cfg
[nosetests]
where=mysite/blog/
(See the documentation for what parameters you can put here).
I'm not sure that this will work (it is possible that the command that starts nose has already specified a different configuration file to use), but it seems worth a shot.

"File not found" using manage.py with pytest-django

I have a Django project in which certain environment variables are set in manage.py which later serve as the values defined in settings.py. Therefore, in order to run pytest-django, I'd like to run manage.py first.
I'm trying to follow the instructions in https://pytest-django.readthedocs.io/en/latest/faq.html#how-can-i-use-manage-py-test-with-pytest-django, but I'm running into an unexpected error. I have the following directory structure:
.
├── lucy
│   ├── settings
│   ├── base.py
│   ├── development.py
│   ├── production.py
│   └── staging.py
│   ├── staticfiles
│   ├── urls.py
│   └── wsgi.py
├── lucy_web
│   ├── __init__.py
│   ├── actions.py
│   ├── admin
│   ├── apps.py
│   ├── fixtures
│   ├── forms
│   ├── lib
│   ├── management
│   ├── migrations
│   ├── models
│   ├── runner.py
│   ├── serializers.py
│   ├── static
│   ├── templates
│   ├── templatetags
│   ├── tests
│   ├── urls.py
│   └── views
├── manage.py
├── pytest.ini
The contents of runner.py are taken exactly from the FAQ:
class PytestTestRunner(object):
"""Runs pytest to discover and run tests."""
def __init__(self, verbosity=1, failfast=False, keepdb=False, **kwargs):
self.verbosity = verbosity
self.failfast = failfast
self.keepdb = keepdb
def run_tests(self, test_labels):
"""Run pytest and return the exitcode.
It translates some of Django's test command option to pytest's.
"""
import pytest
argv = []
if self.verbosity == 0:
argv.append('--quiet')
if self.verbosity == 2:
argv.append('--verbose')
if self.verbosity == 3:
argv.append('-vv')
if self.failfast:
argv.append('--exitfirst')
if self.keepdb:
argv.append('--reuse-db')
argv.extend(test_labels)
return pytest.main(argv)
Finally, in lucy/settings/base.py I add the following line:
TEST_RUNNER = 'lucy_web.runner.PytestTestRunner'
Finally, the pytest.ini file is as in the example in the docs:
# -- FILE: pytest.ini (or tox.ini)
[pytest]
DJANGO_SETTINGS_MODULE = lucy.settings.production
# -- recommended but optional:
python_files = tests.py test_*.py *_tests.py
The problem is that pytest doesn't seem able to find the tests. If I run the command
python manage.py test lucy_web.tests
I get
(venv) Kurts-MacBook-Pro:lucy-web kurtpeek$ python manage.py test lucy_web.tests
============================================ test session starts ============================================
platform darwin -- Python 3.6.4, pytest-3.3.2, py-1.5.2, pluggy-0.6.0
rootdir: /Users/kurtpeek/Documents/Dev/lucy/lucy-web, inifile: pytest.ini
======================================= no tests ran in 0.00 seconds ========================================
ERROR: file not found: lucy_web.tests
However, if I comment out the TEST_RUNNER line in base.py and run the same command, the tests run successfully:
(venv) Kurts-MacBook-Pro:lucy-web kurtpeek$ python manage.py test lucy_web.tests
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
....E.F................
----------------------------------------------------------------------
Ran 23 tests in 15.974s
FAILED (failures=1, errors=1)
Destroying test database for alias 'default'...
What am I doing wrong here? Do I need to put runner.py in a different location?
Maybe it's because of __init__.py
I experienced a similar situation when using pytest. I deleted __init__.py in tests dir and it tested well.
It's little bit different situation, but I hope this helps.
links that I got help.
https://docs.pytest.org/en/latest/pythonpath.html
`py.test` and `__init__.py` files

Categories