Commit 2586f09d by Usman Khalid Committed by Renzo Lucioni

Add option to disable migrations when running tests

By default, migrations are applied as they always have been. Exporting DISABLE_MIGRATIONS=1 or passing --disable-migrations to Paver commands will create tables directly from apps' models.
parent db5fe130
...@@ -23,6 +23,7 @@ import os ...@@ -23,6 +23,7 @@ import os
from path import Path as path from path import Path as path
from warnings import filterwarnings, simplefilter from warnings import filterwarnings, simplefilter
from uuid import uuid4 from uuid import uuid4
from util.db import NoOpMigrationModules
# import settings from LMS for consistent behavior with CMS # import settings from LMS for consistent behavior with CMS
# pylint: disable=unused-import # pylint: disable=unused-import
...@@ -42,7 +43,7 @@ MONGO_HOST = os.environ.get('EDXAPP_TEST_MONGO_HOST', 'localhost') ...@@ -42,7 +43,7 @@ MONGO_HOST = os.environ.get('EDXAPP_TEST_MONGO_HOST', 'localhost')
THIS_UUID = uuid4().hex[:5] THIS_UUID = uuid4().hex[:5]
# Nose Test Runner # Nose Test Runner
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' TEST_RUNNER = 'openedx.core.djangolib.nose.NoseTestSuiteRunner'
_SYSTEM = 'cms' _SYSTEM = 'cms'
...@@ -129,9 +130,10 @@ DATABASES = { ...@@ -129,9 +130,10 @@ DATABASES = {
}, },
} }
# This hack disables migrations during tests. We want to create tables directly from the models for speed. if os.environ.get('DISABLE_MIGRATIONS'):
# See https://groups.google.com/d/msg/django-developers/PWPj3etj3-U/kCl6pMsQYYoJ. # Create tables directly from apps' models. This can be removed once we upgrade
MIGRATION_MODULES = {app: "app.migrations_not_used_in_tests" for app in INSTALLED_APPS} # to Django 1.9, which allows setting MIGRATION_MODULES to None in order to skip migrations.
MIGRATION_MODULES = NoOpMigrationModules()
LMS_BASE = "localhost:8000" LMS_BASE = "localhost:8000"
FEATURES['PREVIEW_LMS_BASE'] = "preview" FEATURES['PREVIEW_LMS_BASE'] = "preview"
......
...@@ -231,3 +231,16 @@ def generate_int_id(minimum=0, maximum=MYSQL_MAX_INT, used_ids=None): ...@@ -231,3 +231,16 @@ def generate_int_id(minimum=0, maximum=MYSQL_MAX_INT, used_ids=None):
cid = random.randint(minimum, maximum) cid = random.randint(minimum, maximum)
return cid return cid
class NoOpMigrationModules(object):
"""
Return invalid migrations modules for apps. Used for disabling migrations during tests.
See https://groups.google.com/d/msg/django-developers/PWPj3etj3-U/kCl6pMsQYYoJ.
"""
def __contains__(self, item):
return True
def __getitem__(self, item):
return "notmigrations"
...@@ -24,6 +24,7 @@ from path import Path as path ...@@ -24,6 +24,7 @@ from path import Path as path
from uuid import uuid4 from uuid import uuid4
from warnings import filterwarnings, simplefilter from warnings import filterwarnings, simplefilter
from util.db import NoOpMigrationModules
from openedx.core.lib.tempdir import mkdtemp_clean from openedx.core.lib.tempdir import mkdtemp_clean
# This patch disables the commit_on_success decorator during tests # This patch disables the commit_on_success decorator during tests
...@@ -86,7 +87,7 @@ PARENTAL_CONSENT_AGE_LIMIT = 13 ...@@ -86,7 +87,7 @@ PARENTAL_CONSENT_AGE_LIMIT = 13
SOUTH_TESTS_MIGRATE = False # To disable migrations and use syncdb instead SOUTH_TESTS_MIGRATE = False # To disable migrations and use syncdb instead
# Nose Test Runner # Nose Test Runner
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' TEST_RUNNER = 'openedx.core.djangolib.nose.NoseTestSuiteRunner'
_SYSTEM = 'lms' _SYSTEM = 'lms'
...@@ -188,9 +189,10 @@ DATABASES = { ...@@ -188,9 +189,10 @@ DATABASES = {
} }
# This hack disables migrations during tests. We want to create tables directly from the models for speed. if os.environ.get('DISABLE_MIGRATIONS'):
# See https://groups.google.com/d/msg/django-developers/PWPj3etj3-U/kCl6pMsQYYoJ. # Create tables directly from apps' models. This can be removed once we upgrade
MIGRATION_MODULES = {app: "app.migrations_not_used_in_tests" for app in INSTALLED_APPS} # to Django 1.9, which allows setting MIGRATION_MODULES to None in order to skip migrations.
MIGRATION_MODULES = NoOpMigrationModules()
CACHES = { CACHES = {
# This is the cache used for most things. # This is the cache used for most things.
......
"""
Utilities related to nose.
"""
from django.core.management import call_command
from django.db import DEFAULT_DB_ALIAS, connections, transaction
import django_nose
class NoseTestSuiteRunner(django_nose.NoseTestSuiteRunner):
"""Custom NoseTestSuiteRunner."""
def setup_databases(self):
""" Setup databases and then flush to remove data added by migrations. """
return_value = super(NoseTestSuiteRunner, self).setup_databases()
# Delete all data added by data migrations. Unit tests should setup their own data using factories.
call_command('flush', verbosity=0, interactive=False, load_initial_data=False)
# Through Django 1.8, auto increment sequences are not reset when calling flush on a SQLite db.
# So we do it ourselves.
# http://sqlite.org/autoinc.html
connection = connections[DEFAULT_DB_ALIAS]
if connection.vendor == 'sqlite' and not connection.features.supports_sequence_reset:
with transaction.atomic(using=DEFAULT_DB_ALIAS):
cursor = connection.cursor()
cursor.execute(
"delete from sqlite_sequence;"
)
return return_value
...@@ -34,6 +34,12 @@ __test__ = False # do not collect ...@@ -34,6 +34,12 @@ __test__ = False # do not collect
make_option("-q", "--quiet", action="store_const", const=0, dest="verbosity"), make_option("-q", "--quiet", action="store_const", const=0, dest="verbosity"),
make_option("-v", "--verbosity", action="count", dest="verbosity", default=1), make_option("-v", "--verbosity", action="count", dest="verbosity", default=1),
make_option("--pdb", action="store_true", help="Drop into debugger on failures or errors"), make_option("--pdb", action="store_true", help="Drop into debugger on failures or errors"),
make_option(
'--disable-migrations',
action='store_true',
dest='disable_migrations',
help="Create tables directly from apps' models. Can also be used by exporting DISABLE_MIGRATIONS=1."
),
], share_with=['pavelib.utils.test.utils.clean_reports_dir']) ], share_with=['pavelib.utils.test.utils.clean_reports_dir'])
def test_system(options): def test_system(options):
""" """
...@@ -51,6 +57,7 @@ def test_system(options): ...@@ -51,6 +57,7 @@ def test_system(options):
'cov_args': getattr(options, 'cov_args', ''), 'cov_args': getattr(options, 'cov_args', ''),
'skip_clean': getattr(options, 'skip_clean', False), 'skip_clean': getattr(options, 'skip_clean', False),
'pdb': getattr(options, 'pdb', False), 'pdb': getattr(options, 'pdb', False),
'disable_migrations': getattr(options, 'disable_migrations', False),
} }
if test_id: if test_id:
...@@ -134,6 +141,12 @@ def test_lib(options): ...@@ -134,6 +141,12 @@ def test_lib(options):
make_option("-q", "--quiet", action="store_const", const=0, dest="verbosity"), make_option("-q", "--quiet", action="store_const", const=0, dest="verbosity"),
make_option("-v", "--verbosity", action="count", dest="verbosity", default=1), make_option("-v", "--verbosity", action="count", dest="verbosity", default=1),
make_option("--pdb", action="store_true", help="Drop into debugger on failures or errors"), make_option("--pdb", action="store_true", help="Drop into debugger on failures or errors"),
make_option(
'--disable-migrations',
action='store_true',
dest='disable_migrations',
help="Create tables directly from apps' models. Can also be used by exporting DISABLE_MIGRATIONS=1."
),
]) ])
def test_python(options): def test_python(options):
""" """
...@@ -146,6 +159,7 @@ def test_python(options): ...@@ -146,6 +159,7 @@ def test_python(options):
'extra_args': getattr(options, 'extra_args', ''), 'extra_args': getattr(options, 'extra_args', ''),
'cov_args': getattr(options, 'cov_args', ''), 'cov_args': getattr(options, 'cov_args', ''),
'pdb': getattr(options, 'pdb', False), 'pdb': getattr(options, 'pdb', False),
'disable_migrations': getattr(options, 'disable_migrations', False),
} }
python_suite = suites.PythonTestSuite('Python Tests', **opts) python_suite = suites.PythonTestSuite('Python Tests', **opts)
......
""" """
Classes used for defining and running python test suites Classes used for defining and running python test suites
""" """
import os
from pavelib.utils.test import utils as test_utils from pavelib.utils.test import utils as test_utils
from pavelib.utils.test.suites.suite import TestSuite from pavelib.utils.test.suites.suite import TestSuite
from pavelib.utils.test.suites.nose_suite import LibTestSuite, SystemTestSuite from pavelib.utils.test.suites.nose_suite import LibTestSuite, SystemTestSuite
...@@ -16,11 +18,16 @@ class PythonTestSuite(TestSuite): ...@@ -16,11 +18,16 @@ class PythonTestSuite(TestSuite):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(PythonTestSuite, self).__init__(*args, **kwargs) super(PythonTestSuite, self).__init__(*args, **kwargs)
self.opts = kwargs self.opts = kwargs
self.disable_migrations = kwargs.get('disable_migrations', False)
self.fasttest = kwargs.get('fasttest', False) self.fasttest = kwargs.get('fasttest', False)
self.subsuites = kwargs.get('subsuites', self._default_subsuites) self.subsuites = kwargs.get('subsuites', self._default_subsuites)
def __enter__(self): def __enter__(self):
super(PythonTestSuite, self).__enter__() super(PythonTestSuite, self).__enter__()
if self.disable_migrations:
os.environ['DISABLE_MIGRATIONS'] = '1'
if not (self.fasttest or self.skip_clean): if not (self.fasttest or self.skip_clean):
test_utils.clean_test_files() test_utils.clean_test_files()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment