Allow tests to be run together with other apps.

This commit is contained in:
Jacek Tomaszewski 2012-11-10 18:47:03 +01:00
parent 8d9d837b2f
commit c7b8b77e86
5 changed files with 189 additions and 53 deletions

View file

@ -1,15 +1,16 @@
# -*- coding: utf-8 -*-
"""
Tests have to be run with modeltranslation.tests.settings:
./manage.py test --settings=modeltranslation.tests.settings modeltranslation
TODO: Merge autoregister tests from django-modeltranslation-wrapper.
NOTE: Perhaps ModeltranslationTestBase in tearDownClass should reload some modules,
so that tests for other apps are in the same environment.
"""
import os
import shutil
from django import forms
from django.conf import settings
from django.conf import settings as django_settings
from django.contrib.admin.sites import AdminSite
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
@ -28,16 +29,86 @@ from modeltranslation.tests.models import (
FileFieldsModel, TestModel, MultitableBModelA, MultitableModelC,
MultitableDTestModel)
from modeltranslation.tests.translation import FallbackModel2TranslationOptions
from modeltranslation.tests.test_settings import TEST_SETTINGS
try:
from django.test.utils import override_settings
except ImportError:
from modeltranslation.tests.utils import override_settings
# None of the following tests really depend on the content of the request,
# so we'll just pass in None.
request = None
@override_settings(**TEST_SETTINGS)
class ModeltranslationTestBase(TestCase):
urls = 'modeltranslation.tests.urls'
cache = AppCache()
synced = False
@classmethod
def setUpClass(cls):
"""
Prepare database:
* Call syncdb to create tables for tests.models (since during
default testrunner's db creation modeltranslation.tests was not in INSTALLED_APPS
"""
super(ModeltranslationTestBase, cls).setUpClass()
if not ModeltranslationTestBase.synced:
# In odred to perform only one syncdb
ModeltranslationTestBase.synced = True
with override_settings(**TEST_SETTINGS):
import sys
# 1. Reload translation in case USE_I18N was False
from django.utils import translation
reload(translation)
# 2. Reload MT because LANGUAGES likely changed.
reload(mt_settings)
reload(translator)
from modeltranslation import admin, utils
reload(admin)
reload(utils)
# 3. Reset test models (because autodiscover have already run, those models
# have translation fields, but for languages previously defined. We want
# to be sure that 'de' and 'en' are available)
del cls.cache.app_models['tests']
from modeltranslation.tests import models
reload(models)
cls.cache.load_app('modeltranslation.tests')
sys.modules.pop('modeltranslation.tests.translation', None)
# 4. Autodiscover
from modeltranslation import models
reload(models)
# 5. Reload some imported classes
cls.reload_globals('modeltranslation.tests.models')
cls.reload_globals('modeltranslation.admin')
# 6. Syncdb (``migrate=False`` in case of south)
from django.db import connections, DEFAULT_DB_ALIAS
from django.core.management import call_command
call_command('syncdb', verbosity=0, migrate=False, interactive=False,
database=connections[DEFAULT_DB_ALIAS].alias, load_initial_data=False)
@staticmethod
def reload_globals(module):
"""
Very ugly method for reloading things imported from module.
It wouldn't be needed if eg. ``TestModel`` calls would be replaced by ``models.TestModel``.
"""
names = []
for name, item in globals().items():
if hasattr(item, '__module__') and item.__module__ == module:
names.append(name)
_temp = __import__(module, globals(), locals(), names, -1)
for name in names:
globals()[name] = getattr(_temp, name)
@classmethod
def clear_cache(cls):
@ -71,17 +142,13 @@ class ModeltranslationTestBase(TestCase):
class ModeltranslationTest(ModeltranslationTestBase):
"""Basic tests for the modeltranslation application."""
def test_registration(self):
self.client.post('/set_language/', data={'language': 'de'})
#self.client.session['django_language'] = 'de-de'
#self.client.cookies[settings.LANGUAGE_COOKIE_NAME] = 'de-de'
langs = tuple(l[0] for l in settings.LANGUAGES)
langs = tuple(l[0] for l in django_settings.LANGUAGES)
self.failUnlessEqual(2, len(langs))
self.failUnless('de' in langs)
self.failUnless('en' in langs)
self.failUnless(translator.translator)
# Check that eight models are registered for translation
# Check that nine models are registered for translation
self.failUnlessEqual(len(translator.translator._registry), 9)
# Try to unregister a model that is not registered

View file

@ -1,59 +1,29 @@
# -*- coding: utf-8 -*-
"""
Settings overrided for test time
"""
import os
import django
from django.conf import settings
DIRNAME = os.path.dirname(__file__)
DEBUG = False
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:'
}
}
test_db = os.environ.get('DB', 'sqlite')
if test_db == 'mysql':
DATABASES['default'].update({
'ENGINE': 'django.db.backends.mysql',
'NAME': 'modeltranslation',
'USER': 'root',
})
elif test_db == 'postgres':
DATABASES['default'].update({
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'USER': 'postgres',
'NAME': 'modeltranslation',
'OPTIONS': {
'autocommit': True,
}
})
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.admin',
'django.contrib.sites',
'django.contrib.redirects',
'modeltranslation',
INSTALLED_APPS = tuple(settings.INSTALLED_APPS) + (
'modeltranslation.tests',
)
if django.VERSION[0] >= 1 and django.VERSION[1] >= 3:
INSTALLED_APPS += ('django.contrib.staticfiles',)
# IMO this is unimportant
#if django.VERSION[0] >= 1 and django.VERSION[1] >= 3:
#INSTALLED_APPS += ('django.contrib.staticfiles',)
STATIC_URL = '/static/'
ROOT_URLCONF = 'modeltranslation.tests.urls'
#STATIC_URL = '/static/'
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(DIRNAME, 'media/')
SITE_ID = 1
LANGUAGES = (('de', 'Deutsch'),
('en', 'English'))
LANGUAGE_CODE = 'de'
DEFAULT_LANGUAGE = 'de'
SECRET_KEY = 'z5)t*g3003n23lze(&u5)kt&9gtcf)*-o++m2#n-w!qts7u1cj'
USE_I18N = True

View file

@ -0,0 +1,6 @@
"""
Get test settings in dict format (for use with settings_override).
"""
import settings as _settings
TEST_SETTINGS = dict((k, getattr(_settings, k)) for k in dir(_settings) if k == k.upper())

View file

@ -0,0 +1,60 @@
"""
This is Django 1.4 override_settings decorator backported for compatibility with Django 1.3.
The only difference is that this version does not use settings_changes signal
(because there is no such signal).
"""
from django.conf import settings, UserSettingsHolder
class override_settings(object):
"""
Acts as either a decorator, or a context manager. If it's a decorator it
takes a function and returns a wrapped function. If it's a contextmanager
it's used with the ``with`` statement. In either event entering/exiting
are called before and after, respectively, the function/block is executed.
"""
def __init__(self, **kwargs):
self.options = kwargs
self.wrapped = settings._wrapped
def __enter__(self):
self.enable()
def __exit__(self, exc_type, exc_value, traceback):
self.disable()
def __call__(self, test_func):
from django.test import TransactionTestCase
if isinstance(test_func, type) and issubclass(test_func, TransactionTestCase):
original_pre_setup = test_func._pre_setup
original_post_teardown = test_func._post_teardown
def _pre_setup(innerself):
self.enable()
original_pre_setup(innerself)
def _post_teardown(innerself):
original_post_teardown(innerself)
self.disable()
test_func._pre_setup = _pre_setup
test_func._post_teardown = _post_teardown
return test_func
else:
@wraps(test_func)
def inner(*args, **kwargs):
with self:
return test_func(*args, **kwargs)
return inner
def enable(self):
override = UserSettingsHolder(settings._wrapped)
for key, new_value in self.options.items():
setattr(override, key, new_value)
settings._wrapped = override
def disable(self):
settings._wrapped = self.wrapped
for key in self.options:
new_value = getattr(settings, key, None)

View file

@ -2,11 +2,44 @@
import os
import sys
from django.conf import settings
from django.core.management import call_command
os.environ['DJANGO_SETTINGS_MODULE'] = 'modeltranslation.tests.settings'
def runtests():
if not settings.configured:
# Choose database for settings
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:'
}
}
test_db = os.environ.get('DB', 'sqlite')
if test_db == 'mysql':
DATABASES['default'].update({
'ENGINE': 'django.db.backends.mysql',
'NAME': 'modeltranslation',
'USER': 'root',
})
elif test_db == 'postgres':
DATABASES['default'].update({
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'USER': 'postgres',
'NAME': 'modeltranslation',
'OPTIONS': {
'autocommit': True,
}
})
# Configure test environment
settings.configure(
DATABASES = DATABASES,
INSTALLED_APPS = (
'modeltranslation',
),
ROOT_URLCONF = None, # tests override urlconf, but it still needs to be defined
)
failures = call_command(
'test', 'modeltranslation', interactive=False, failfast=False,
verbosity=2)