How to launch tests for django reusable app? How to launch tests for django reusable app? django django

How to launch tests for django reusable app?


The correct usage of Django (>= 1.4) test runner is as follows:

import django, sysfrom django.conf import settingssettings.configure(DEBUG=True,               DATABASES={                    'default': {                        'ENGINE': 'django.db.backends.sqlite3',                    }                },               ROOT_URLCONF='myapp.urls',               INSTALLED_APPS=('django.contrib.auth',                              'django.contrib.contenttypes',                              'django.contrib.sessions',                              'django.contrib.admin',                              'myapp',))try:    # Django < 1.8    from django.test.simple import DjangoTestSuiteRunner    test_runner = DjangoTestSuiteRunner(verbosity=1)except ImportError:    # Django >= 1.8    django.setup()    from django.test.runner import DiscoverRunner    test_runner = DiscoverRunner(verbosity=1)failures = test_runner.run_tests(['myapp'])if failures:    sys.exit(failures)

DjangoTestSuiteRunner and DiscoverRunner have mostly compatible interfaces.

For more information you should consult the "Defining a Test Runner" docs:


I've ended with such solution (it was inspired by solution found in django-voting):

Create file eg. 'runtests.py' in tests dir containing:

import os, sysfrom django.conf import settingsDIRNAME = os.path.dirname(__file__)settings.configure(DEBUG = True,                   DATABASE_ENGINE = 'sqlite3',                   DATABASE_NAME = os.path.join(DIRNAME, 'database.db'),                   INSTALLED_APPS = ('django.contrib.auth',                                     'django.contrib.contenttypes',                                     'django.contrib.sessions',                                     'django.contrib.admin',                                     'myapp',                                     'myapp.tests',))from django.test.simple import run_testsfailures = run_tests(['myapp',], verbosity=1)if failures:    sys.exit(failures)

It allows to run tests by python runtests.py command.It doesn't require installed dependencies (eg. buildout) and it doesn't harm tests run when app is incorporated into bigger project.


For Django 1.7 it's slightly different. Assuming you have the followingdirectory structure for app foo:

foo|── docs|── foo│   ├── __init__.py│   ├── models.py│   ├── urls.py│   └── views.py└── tests    ├── foo_models    │   ├── __init__.py    │   ├── ...    │   └── tests.py    ├── foo_views     │   ├── __init__.py    │   ├── ...    │   └── tests.py    ├── runtests.py    └── urls.py

This is how the Django project itself structures its tests.

You want to run all tests in foo/tests/ with the command:

python3 runtests.py

You also want to be able to run the command from a parent directory of tests, e.g. by Tox or Invoke, just like python3 foo/tests/runtests.py.

The solution I present here is quite reusable, only the app's name foo must be adjusted (and additional apps, if necessary). They can not be installed via modify_settings, because it would miss the database setup.

The following files are needed:

urls.py

"""This urlconf exists because Django expects ROOT_URLCONF to exist. URLsshould be added within the test folders, and use TestCase.urls to set them.This helps the tests remain isolated."""urlpatterns = []

runtests.py

#!/usr/bin/env python3import globimport osimport sysimport djangofrom django.conf import settingsfrom django.core.management import execute_from_command_lineBASE_DIR = os.path.abspath(os.path.dirname(__file__))sys.path.append(os.path.abspath(os.path.join(BASE_DIR, '..')))# Unfortunately, apps can not be installed via ``modify_settings``# decorator, because it would miss the database setup.CUSTOM_INSTALLED_APPS = (    'foo',    'django.contrib.admin',)ALWAYS_INSTALLED_APPS = (    'django.contrib.auth',    'django.contrib.contenttypes',    'django.contrib.sessions',    'django.contrib.messages',    'django.contrib.staticfiles',)ALWAYS_MIDDLEWARE_CLASSES = (    'django.contrib.sessions.middleware.SessionMiddleware',    'django.middleware.common.CommonMiddleware',    'django.middleware.csrf.CsrfViewMiddleware',    'django.contrib.auth.middleware.AuthenticationMiddleware',    'django.contrib.messages.middleware.MessageMiddleware',    'django.middleware.clickjacking.XFrameOptionsMiddleware',)settings.configure(    SECRET_KEY="django_tests_secret_key",    DEBUG=False,    TEMPLATE_DEBUG=False,    ALLOWED_HOSTS=[],    INSTALLED_APPS=ALWAYS_INSTALLED_APPS + CUSTOM_INSTALLED_APPS,    MIDDLEWARE_CLASSES=ALWAYS_MIDDLEWARE_CLASSES,    ROOT_URLCONF='tests.urls',    DATABASES={        'default': {            'ENGINE': 'django.db.backends.sqlite3',        }    },    LANGUAGE_CODE='en-us',    TIME_ZONE='UTC',    USE_I18N=True,    USE_L10N=True,    USE_TZ=True,    STATIC_URL='/static/',    # Use a fast hasher to speed up tests.    PASSWORD_HASHERS=(        'django.contrib.auth.hashers.MD5PasswordHasher',    ),    FIXTURE_DIRS=glob.glob(BASE_DIR + '/' + '*/fixtures/'))django.setup()args = [sys.argv[0], 'test']# Current module (``tests``) and its submodules.test_cases = '.'# Allow accessing test options from the command line.offset = 1try:    sys.argv[1]except IndexError:    passelse:    option = sys.argv[1].startswith('-')    if not option:        test_cases = sys.argv[1]        offset = 2args.append(test_cases)# ``verbosity`` can be overwritten from command line.args.append('--verbosity=2')args.extend(sys.argv[offset:])execute_from_command_line(args)

Some of the settings are optional; they improve speed or a more realistic environment.

The second argument points to the current directory. It makes use of the feature of providing a path to a directory to discover tests below that directory.