Unit test for packages outside of django apps - python

In my Django project I have a few packages/folders that are not applications themselves, but contain code that uses Django models and needs to be covered with tests.
Obiously, when I run 'test nonapppackage.testname' I get:
django.core.exceptions.ImproperlyConfigured: App with label xyzutils is
missing a models.py module.
What's the best to configure unittest for out-of-app code?

Related

How to properly test a Django library

I frequently run into practical problems when I want to test a library I develop which is a Django app. Django apps can be developed independently, like DRF, etc.
For testing, you either need a Django project, or at least a settings.py file or the like, see here.
But I don't get how to do that properly for a "standalone" library. I need to generate migrations for that library (if it contains models) - so I need a manage.py file to invoke manage.py makemigrations, I need to check if the lib is integrating into the project properly (in my case apps are full-fledged plugins, I am using GDAPS).
What is the best approach here?
should I create a separate "test project" in a separate repo that uses my library and tests it?
should I create a project within my library's tests directory and use that? CAVE python paths...
should I not use the project at all and mimick everything - how?
Please give me at least a hint in some direction.
Was googling the same question, and found that the "Using the Django test runner to test reusable applications" section of the Advanced testing topics of the Django documentation gives a suitable common practice. It's a mixture of all three your approaches.
Basically you should create a tests package next to the application code, with a test_settings.py containing all the settings your library needs, and at least set the SECRET_KEY and INSTALLED_APPS variables.
SECRET_KEY = 'fake-key'
INSTALLED_APPS = [
"tests", "your-library"
]
Where "your-library" is the name of the libray you need to test.
In my case, I had to also add 'django.contrib.auth', 'django.contrib.contenttypes', as they where needed by my library.
Then you put all your tests into that package, eventual addictional models in a models.py file, and run them with a runtests.py that lives on the root of your repo:
#!/usr/bin/env python
import os
import sys
import django
from django.conf import settings
from django.test.utils import get_runner
if __name__ == "__main__":
os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.test_settings'
django.setup()
TestRunner = get_runner(settings)
test_runner = TestRunner()
failures = test_runner.run_tests(["tests"])
sys.exit(bool(failures))
It will use the test_settings.py as its settings file (os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.test_settings'), and run all the tests contained in the tests package.

Django Django model “doesn't declare an explicit app_label” because of project's init.py file

I have a django 1.11 project with some rest_framework related apps. Writing tests for new app, I have suddenly gotten the issue
'RuntimeError: Model class core.myApp.models.query_record
doesn't declare an explicit app_label and isn't in an
application in INSTALLED_APPS'
I do have this listed in installed_apps, and in the end, the reason I have this issue is because I have an __init.py__ file in the top level of the project that loads some config for some related celery tasks.
I'm unaware why I haven't seen this issue in other app tests, as there is nothing particularly special about this app or it's model. But, this is causing all tests to fail for this app.
So, my question is, is there a way I can run these unit tests and ignore the projects top level __init.py__ ? Or maybe I should ask, is there a non-hacky way to do it?
the project level __init.py__:
from __future__ import absolute_import
from .celeryapp import app as celery_app
All other app init.py files are empty.
A problem might be, that .celeryapp is trying to import some models that aren't loaded yet.
You can try to add a AppConfig to core.myApp and load/import your celery app in it's ready() method. See the Django docs for more information Django docs for more information
It turned out, in the end this was simply because of how I was running tests
I was running tests like this
./manage.py test myApp --pattern=*.py
The pure wildcard was causing import issues. I should have done this:
./manage.py test myApp --pattern=prefix_*.py
D'oh.

How to disable Django's Test Discovery?

This is an unusual situation - most Django users want Django's test runner to find all of their tests.
I'm working on a python library with several test suites that are run with different configurations, so I don't want the discovery to find and run tests from the wrong configuration. How do I disable discovery entirely and rely on the pre-1.6 behavior of only running the tests for apps explicitly declared in INSTALLED_APPS?
My library structure:
library/ # django app used by others
tests/ # custom test suites here
core/ # tests of core functionality
custom/ # tests of a custom feature requiring separate config
contrib/ # tests for assorted contrib features, also requiring separate config
manage_core.py # separate manage.py files for each "project"
manage_custom.py # these specify settings file to use.
manage_contrib.py
settings.py # base settings for all tests
settings_core.py # settings for 'core' tests including unique INSTALLED_APPS
settings_custom.py # settings for 'custom' tests; different INSTALLED_APPS
settings_contrib.py # settings for 'contrib' tests; different INSTALLED_APPS
The problem is that this command, which should only run tests for the 'contrib' test suite, is also finding and running tests for 'core':
./manage_contrib.py test contrib.tests
It's missing from the Django docs, but the command-line has an option, found via ./manage.py help test:
-t TOP_LEVEL, --top-level-directory TOP_LEVEL
Top level of project for unittest discovery.
Confusingly, specifying the module to test doesn't appear to prevent test discovery, but specifying a sub-directory does, like this:
./manage_contrib.py test contrib.tests -t ./contrib/
That appears to prevent the discovery of tests located outside of contrib.
Hmm I'm unfortunately not aware of a settings parameter that might let you tell unittest to only run from individual apps (a-la "settings.TEST_DIRECTORIES=settings.INSTALLED_APPS") but if you're able to give your tests a unique naming convention, you could use the --pattern= option when running the test suite.
For example, if you have
/myapp/tests/test_a_models.py
/myapp/tests/test_b_models.py
You could only run a with ./manage.py test --pattern='*_a_*' and then run b with ./manage.py test --pattern='*_b_*'
Definitely not ideal, but might get the job done depending on how much flexibility you have with the test naming conventions in your own app.

Django 1.7 loads extraneous models when running tests

I have a Django project that worked well with Django 1.6, but is giving me a lot of trouble as I try to upgrade to Django 1.7.
In this project is an app, my_app which does not contain any models of its own, but contains a file other_models.py, generated to interact with a legacy database using python manage.py inspectdb. I want to use these models only in specific code paths, when I'm connected to a this secondary database. Again, this worked fine with Django 1.6.
Having upgraded to Django 1.7, I am no longer able to run my test suite using python manage.py test. When I do this, I get a huge number of errors that look like the following:
CommandError: System check identified some issues:
ERRORS:
my_app.AccountAccount.created_by_type: (fields.E304) Reverse accessor for 'AccountAccount.created_by_type' clashes with reverse accessor for 'AccountAccount.deactivated_by_type'.
HINT: Add or change a related_name argument to the definition for 'AccountAccount.created_by_type' or 'AccountAccount.deactivated_by_type'.
...
# Tons more of these
These errors are all complaining about models defined in other_models.py. It appears to me that the changes in app-loading process are causing my problems, but I'm not entirely sure.
I have tried setting up an apps.py in this app with the following code:
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'my_app'
models_module = None
and added default_app_config = 'my_app.apps.MyAppConfig' to my my_app.__init__.py, as per the documentation on configuring applications, but to no avail.
I'm at a total loss for what to do next, and would appreciate any information regarding how to control when and how Django attempts to load models, especially when running python manage.py test.

Django 1.7 - makemigrations not detecting changes - managed models

I have just installed django 1.7 in my virtual env.
Then I manually created the following files:
service_bus/
service_bus/__init__.py
service_bus/django_settings.py
service_bus/models
service_bus/models/__init__.py
service_bus/models/dsp.py
service_bus/models/audience_type.py
service_bus/models/category.py
service_bus/models/audience.py
service_bus/models/dsp_config.py
service_bus/models/apsettings.py
So I have a settings file service_bus/django_settings.py and the service_bus app.
Then I did, on bash:
export DJANGO_SETTINGS_MODULE='service_bus.django_settings'
Then I just try to run makemigrations, but it says no changes are detected.
$ django-admin makemigrations
Loading properties from /etc/s1mbi0se/dmp.ini
System check identified some issues:
WARNINGS:
?: (1_6.W001) Some project unittests may not execute as expected.
HINT: Django 1.6 introduced a new default test runner. It looks like this project was generated using Django 1.5 or earlier. You should ensure your tests are all running & behaving as expected. See https://docs.djangoproject.com/en/dev/releases/1.6/#new-test-runner for more information.
No changes detected
$ django-admin makemigrations service_bus
Loading properties from /etc/s1mbi0se/dmp.ini
System check identified some issues:
WARNINGS:
?: (1_6.W001) Some project unittests may not execute as expected.
HINT: Django 1.6 introduced a new default test runner. It looks like this project was generated using Django 1.5 or earlier. You should ensure your tests are all running & behaving as expected. See https://docs.djangoproject.com/en/dev/releases/1.6/#new-test-runner for more information.
No changes detected in app 'service_bus'
In all my models I have something like
class APSettings(models.Model):
...
class Meta:
db_table = u'APSettings'
app_label = 'service_bus'
What could I be missing?
You need to run the migrate command first to scaffold the database schema. Then, you can run makemigrations for each app. Check the Django tutorial for more on this.
Make sure you update your models.py file to actually import the models. For example in models.py you'd have from service_bus.models.audience import *. The manage script goes over that file, imports all the models in audience.py and detects changes in there. If you did not add your new models to models.py then the manage script won't know about the new models in you models files.

Categories