Skip to content

Commit

Permalink
Add decorators for skipping tests when run without indicated Django s…
Browse files Browse the repository at this point in the history
…upport.

Many unit tests can only be run if the version of Django includes
certain support. For instance, migration-related unit tests require
support for migrations in Django. Previously, every such unit test had
to have its own checks and handle raising a `SkipTest`, but this was
getting tedious to maintain, and isn't very future-proof.

This change adds some new decorators for these tests, and standardizes
the `SkipTest` message. This simplifies much of the code and improves
maintainability going forward.

Testing Done:
All unit tests pass across all versions of Django.

Verified that the skip message output was correct in older versions.

Reviewed at https://reviews.reviewboard.org/r/11248/
  • Loading branch information
chipx86 committed Oct 30, 2020
1 parent 8d60c8f commit 0daf8e4
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 111 deletions.
122 changes: 122 additions & 0 deletions django_evolution/tests/decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
"""Decorators to aid in creating unit tests.
Version Added:
2.1
"""

from __future__ import unicode_literals

from functools import wraps
from unittest import SkipTest

import django

from django_evolution.support import (supports_constraints,
supports_index_together,
supports_indexes,
supports_migrations)


def _build_requires_support_decorator(flag, skip_message):
"""Build a decorator checking for Django support for a feature.
Args:
flag (bool):
The support flag to check.
skip_message (unicode):
The skip message. This should have ``%(django_version)s`` in the
message somewhere.
Raises:
unittest.SkipTest:
Raised if run on a version of Django without the required support.
"""
def _decorator(func):
@wraps(func)
def _wrapper(*args, **kwargs):
if not flag:
raise SkipTest(skip_message % {
'django_version': '%s.%s' % django.VERSION[:2],
})

return func(*args, **kwargs)

return _wrapper

return _decorator


#: Decorator to require Meta.constraints support for running a test.
#:
#: Args:
#: func (callable):
#: The unit test function to wrap.
#:
#: Raises:
#: unittest.SkipTest:
#: The current version of Django doesn't support ``Meta.constraints``.
requires_meta_constraints = _build_requires_support_decorator(
flag=supports_constraints,
skip_message=("Meta.constraints isn't supported on Django "
"%(django_version)s"))


#: Decorator to require Meta.index_together support for running a test.
#:
#: Args:
#: func (callable):
#: The unit test function to wrap.
#:
#: Raises:
#: unittest.SkipTest:
#: The current version of Django doesn't support
#: ``Meta.index_together``.
requires_meta_index_together = _build_requires_support_decorator(
flag=supports_index_together,
skip_message=("Meta.index_together isn't supported on Django "
"%(django_version)s"))


#: Decorator to require Meta.indexes support for running a test.
#:
#: Args:
#: func (callable):
#: The unit test function to wrap.
#:
#: Raises:
#: unittest.SkipTest:
#: The current version of Django doesn't support ``Meta.indexes``.
requires_meta_indexes = _build_requires_support_decorator(
flag=supports_indexes,
skip_message="Meta.indexes isn't supported on Django %(django_version)s")


#: Decorator to require migrations support for running a test.
#:
#: Args:
#: func (callable):
#: The unit test function to wrap.
#:
#: Raises:
#: unittest.SkipTest:
#: The current version of Django doesn't support migrations.
requires_migrations = _build_requires_support_decorator(
flag=supports_migrations,
skip_message="Migrations aren't supported on Django %(django_version)s")


#: Decorator to require migration history checks for running a test.
#:
#: Args:
#: func (callable):
#: The unit test function to wrap.
#:
#: Raises:
#: unittest.SkipTest:
#: The current version of Django doesn't support performing history
#: checks when running migrations.
requires_migration_history_checks = _build_requires_support_decorator(
flag=(django.VERSION >= (1, 10)),
skip_message=("Migration history checks aren't supported on Django "
"%(django_version)s"))
22 changes: 7 additions & 15 deletions django_evolution/tests/test_change_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

from django.db import models
from django.db.models import Q
from nose import SkipTest

try:
# Django >= 2.2
Expand All @@ -23,9 +22,11 @@

from django_evolution.mutations import ChangeMeta
from django_evolution.support import (supports_constraints,
supports_indexes,
supports_index_together)
supports_indexes)
from django_evolution.tests.base_test_case import EvolutionTestCase
from django_evolution.tests.decorators import (requires_meta_constraints,
requires_meta_index_together,
requires_meta_indexes)
from django_evolution.tests.models import BaseTestModel


Expand Down Expand Up @@ -111,13 +112,10 @@ class ChangeMetaConstraintsTests(EvolutionTestCase):
)

@classmethod
@requires_meta_constraints
def setUpClass(cls):
super(ChangeMetaConstraintsTests, cls).setUpClass()

if not supports_constraints:
raise SkipTest('Meta.constraints is not supported on this version '
'of Django')

def test_keeping_empty(self):
"""Testing ChangeMeta(constraints) and keeping list empty"""
class DestModel(BaseTestModel):
Expand Down Expand Up @@ -372,13 +370,10 @@ class ChangeMetaIndexesTests(EvolutionTestCase):
)

@classmethod
@requires_meta_indexes
def setUpClass(cls):
super(ChangeMetaIndexesTests, cls).setUpClass()

if not supports_indexes:
raise SkipTest('Meta.indexes is not supported on this version '
'of Django')

def test_keeping_empty(self):
"""Testing ChangeMeta(indexes) and keeping list empty"""
class DestModel(BaseTestModel):
Expand Down Expand Up @@ -580,13 +575,10 @@ class ChangeMetaIndexTogetherTests(EvolutionTestCase):
)

@classmethod
@requires_meta_index_together
def setUpClass(cls):
super(ChangeMetaIndexTogetherTests, cls).setUpClass()

if not supports_index_together:
raise SkipTest('Meta.index_together is not supported on this '
'version of Django')

def test_keeping_empty(self):
"""Testing ChangeMeta(index_together) and keeping list empty"""
class DestModel(BaseTestModel):
Expand Down
11 changes: 3 additions & 8 deletions django_evolution/tests/test_evolution_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@

from __future__ import unicode_literals

from unittest import SkipTest

from django.db import DEFAULT_DB_ALIAS, connections

from django_evolution.compat.apps import get_app
from django_evolution.models import Evolution, Version
from django_evolution.support import supports_migrations
from django_evolution.tests.base_test_case import (MigrationsTestsMixin,
TestCase)
from django_evolution.tests.decorators import requires_migrations
from django_evolution.tests.evolutions_app.models import EvolutionsAppTestModel
from django_evolution.tests.evolutions_app2.models import \
EvolutionsApp2TestModel
Expand Down Expand Up @@ -153,11 +152,9 @@ def test_add_evolutions(self):
'app': app,
})

@requires_migrations
def test_add_migration_plan(self):
"""Testing EvolutionGraph.add_migration_plan"""
if not supports_migrations:
raise SkipTest('Not used on Django < 1.7')

class TestsInitialMigration(migrations.Migration):
pass

Expand Down Expand Up @@ -290,11 +287,9 @@ def test_mark_evolutions_applied(self):
'app': app,
})

@requires_migrations
def test_mark_migrations_applied(self):
"""Testing EvolutionGraph.mark_migrations_applied"""
if not supports_migrations:
raise SkipTest('Not used on Django < 1.7')

class TestsInitialMigration(migrations.Migration):
pass

Expand Down
20 changes: 5 additions & 15 deletions django_evolution/tests/test_evolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
# Django < 1.7
migrations = None

from nose import SkipTest

from django_evolution.compat.apps import (get_app,
get_apps,
register_app_models)
Expand All @@ -33,7 +31,6 @@
created_models,
creating_models)
from django_evolution.signature import AppSignature, ModelSignature
from django_evolution.support import supports_migrations
from django_evolution.tests import models as evo_test
from django_evolution.tests.evolutions_app import models as test_app2
from django_evolution.tests.base_test_case import (EvolutionTestCase,
Expand All @@ -43,6 +40,7 @@
execute_test_sql,
register_models)
from django_evolution.utils.apps import get_app_label
from django_evolution.tests.decorators import requires_migrations
from django_evolution.utils.migrations import (MigrationList,
record_applied_migrations)

Expand Down Expand Up @@ -611,13 +609,11 @@ def tearDown(self):
creating_models.disconnect(self._on_creating_models)
created_models.disconnect(self._on_created_models)

@requires_migrations
def test_prepare_tasks_with_migrations_new_app(self):
"""Testing EvolveAppTask.prepare_tasks with migrations for new
app
"""
if not supports_migrations:
raise SkipTest('Not used on Django < 1.7')

class MigrationTestModel(BaseTestModel):
field1 = models.IntegerField()
field3 = models.BooleanField()
Expand Down Expand Up @@ -703,13 +699,11 @@ class RemoveFieldMigration(migrations.Migration):
('tests', '0003_remove_field'),
])

@requires_migrations
def test_prepare_tasks_with_migrations_some_applied(self):
"""Testing EvolveAppTask.prepare_tasks with migrations and some
already applied
"""
if not supports_migrations:
raise SkipTest('Not used on Django < 1.7')

class MigrationTestModel(BaseTestModel):
field1 = models.IntegerField()
field3 = models.BooleanField()
Expand Down Expand Up @@ -794,11 +788,9 @@ class RemoveFieldMigration(migrations.Migration):
('tests', '0003_remove_field'),
])

@requires_migrations
def test_execute_tasks_with_migrations(self):
"""Testing EvolveAppTask.execute_tasks with migrations"""
if not supports_migrations:
raise SkipTest('Not used on Django < 1.7')

class MigrationTestModel(BaseTestModel):
field1 = models.IntegerField()
field2 = models.CharField(max_length=10)
Expand Down Expand Up @@ -895,12 +887,10 @@ class AddFieldMigration(migrations.Migration):
field2='foo',
field3=True)

@requires_migrations
def test_execute_tasks_with_evolutions_and_migrations(self):
"""Testing EvolveAppTask.execute_tasks with evolutions and migrations
"""
if not supports_migrations:
raise SkipTest('Not used on Django < 1.7')

class EvolveMigrateTestModel(BaseTestModel):
field1 = models.IntegerField()

Expand Down

0 comments on commit 0daf8e4

Please sign in to comment.