Reputation: 71
Under my Django project there are a few apps and all of them have unit tests. One of them that I'm working right now is supposed to be included only in dev/stage environments, so I'm enabling it using a environment variable.
When this variable is present it is added to INSTALLED_APPS and it is working just fine, the problem is that Django is executing the tests for this app even when it is not in INSTALLED_APPS, and it fails with the following message:
ImportError: Failed to import test module: debug.tests.unit.test_services`
...(traceback information)...
RuntimeError: Model class debug.models.Email doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.
When I define the app_label
in the class Meta
of models in this app the error is different, it says it can't find a table, I assume that this is because the app is not in INSTALLED_APPS, so it's migrations are not executed.
OperationalError: no such table: debug_email
I'm not sure why Django executes the tests for all apps, but not it's migrations.
Am I missing something from Django configuration for tests?
Upvotes: 7
Views: 4834
Reputation: 1236
https://docs.python.org/3/library/unittest.html#unittest.TestLoader.discover says:
If load_tests exists then discovery does not recurse into the package, load_tests is responsible for loading all tests in the package.
So in the lowest __init__.py
in your app which you don't always want run:
from django.apps import apps
def load_tests(loader, tests, pattern):
from django.conf import settings
if apps.is_installed("your_dev_app"):
# Actually load the tests - thanks to @barney-szabolcs
return loader.discover(start_dir=dirname(abspath(__file__)), pattern=pattern)
Upvotes: 5
Reputation: 12514
You need to return the discovered tests in load_tests
.
So, adding to @DaveLawrence's answer, the complete code is:
# your_dev_app/__init__.py
from django.apps import apps
from os.path import dirname, abspath
def load_tests(loader, tests, pattern):
"""
loads tests for your_dev_app if it is installed.
"""
from django.conf import settings
if apps.is_installed("your_dev_app"):
return loader.discover(start_dir=dirname(abspath(__file__)), pattern=pattern)
Upvotes: 5
Reputation: 12012
When you run:
python manage.py test
the command will look per default recursive for all files with the pattern test*.py
in the working directory. It isn't affected by INSTALLED_APPS
in settings.py
.
You can specify a certain app to test it:
python manage.py test app_label
or specify a path:
python manage.py test myapp/tests
If you want to exclude some tests you can tag them and use the option --exclude-tag
.
Run python manage.py test --help
to get information on all options.
The official documentation gives a lot of information on the different possibilities how to run the tests.
EDIT:
If you have apps that are required only in the development environment, but not in the production, you could split your settings.py
. One possible solution would be to outsource all development settings into a file local_settings.py
and exclude it from versioning or from the production branch, i.e. don't push it in the production environment.
local_settings.py
DEBUG = True
INSTALLED_APPS += (
# Django Debug Toolbar would be for example
# used only in development
'debug_toolbar',
'your dev app',
)
settings.py
try:
from .local_settings import *
except ImportError:
pass
Upvotes: 2