Models inside tests - Django 1.7 issue

2019-04-19 09:04发布

I'm trying to port my project to use Django 1.7. Everything is fine except 1 thing. Models inside tests folders.

Django 1.7 new migrations run migrate command internally. Before syncdb was ran. That means if a model is not included in migrations - it won't be populated to DB (and also to test DB). That's exactly what I'm experiencing right now.

What I do is:

In my /app/tests/models.py I have dummy model: class TestBaseImage(BaseImage): pass All it does is to inherit from an abstract BaseImage model.

Then in tests I create instances of that dummy model to test it.

The problem is that it doesn't work any more. It's not included in migrations (that's obvious as I don't want to keep my test models in a production DB). Running my tests causes DB error saying that table does not exist. That makes sense as it's not included in migrations.

Is there any way to make it work with new migrations system? I can't find a way to "fix" that.

Code I use:

app/tests/models.py

from ..models import BaseImage


class TestBaseImage(BaseImage):
    """Dummy model just to test BaseImage abstract class"""
    pass

app/models.py

class BaseImage(models.Model):
    # ... fields ...
    class Meta:
        abstract = True

factories:

class BaseImageFactory(factory.django.DjangoModelFactory):
    """Factory class for Vessel model"""
    FACTORY_FOR = BaseImage
    ABSTRACT_FACTORY = True


class PortImageFactory(BaseImageFactory):
    FACTORY_FOR = PortImage

example test:

def get_model_field(model, field_name):
    """Returns field instance"""
    return model._meta.get_field_by_name(field_name)[0]


def test_owner_field(self):
    """Tests owner field"""
    field = get_model_field(BaseImage, "owner")

    self.assertIsInstance(field, models.ForeignKey)
    self.assertEqual(field.rel.to, get_user_model())

2条回答
姐就是有狂的资本
2楼-- · 2019-04-19 09:07

There is a ticket requesting a way to do test-only models here

As a workaround, you can decouple your tests.py and make it an app.

tests
|--migrations
|--__init__.py
|--models.py
|--tests.py

You will end up with something like this:

myapp
|-migrations
|-tests
|--migrations
|--__init__.py
|--models.py
|--tests.py
|-__init__.py
|-models.py
|-views.py

Then you should add it to your INSTALLED_APPS

INSTALLED_APPS = (
    # ...
    'myapp',
    'myapp.tests',
)

You probably don't want to install myapp.tests in production, so you can keep separate settings files. Something like this:

INSTALLED_APPS = (
    # ...
    'myapp',
)

try:
    from local_settings import *
except ImportError:
    pass

Or better yet, create a test runner and install your tests there.

Last but not least, remember to run python manage.py makemigrations

查看更多
该账号已被封号
3楼-- · 2019-04-19 09:27

Here's a workaround that seems to work. Trick the migration framework into thinking that there are no migrations for your app. In settings.py:

if 'test' in sys.argv:
    # Only during unittests...

    # myapp uses a test-only model, which won't be loaded if we only load
    # our real migration files, so point to a nonexistent one, which will make
    # the test runner fall back to 'syncdb' behavior.
    MIGRATION_MODULES = {
        'myapp': 'myapp.migrations_not_used_in_tests'
    }

I found the idea on the first post in ths Django dev mailing list thread, and it's also currently being used in Django itself, but it may not work in future versions of Django where migrations are required and the "syncdb fallback" is removed.

查看更多
登录 后发表回答