Django: как создать модель динамически только для тестирования



у меня есть приложение Django, которое требует

658   10  

10 ответов:

вы можете поставить свои тесты в tests/ подкаталог приложения (а не tests.py file), и включить tests/models.py с тестовыми моделями.

затем предоставьте тестовый скрипт (пример) это включает в себя ваш tests/ "приложения" в INSTALLED_APPS. (Это не работает при запуске тестов приложений из реального проекта, в котором не будет приложения тестов в INSTALLED_APPS, но я редко считаю полезным запускать многоразовые тесты приложений из проекта, а Django 1.6+ не делает по умолчанию.)

(Примечание: альтернативный динамический метод, описанный ниже, работает только в Django 1.1+, если ваш тестовый случай подклассы TransactionTestCase - что значительно замедляет ваши тесты - и больше не работает вообще в Django 1.7+. Он остался здесь только для исторического интереса; не используйте его.)

в начале ваших тестов (т. е. в методе настройки или в начале набора тестов) вы можете динамически добавлять "myapp.tests" к установке INSTALLED_APPS, и затем сделайте следующее:

from django.core.management import call_command
from django.db.models import loading
loading.cache.loaded = False
call_command('syncdb', verbosity=0)

затем в конце ваших тестов вы должны очистить, восстановив старую версию INSTALLED_APPS и снова очистив кэш приложения.

этот класс инкапсулирует шаблон, чтобы он не загромождал ваш тестовый код так же сильно.

ответ@paluh требует добавления нежелательного кода в нетестовый файл, и по моему опыту, решение @carl не работает с django.тест.TestCase, который необходим для использования светильников. Если вы хотите использовать django.тест.TestCase, вам нужно убедиться, что вы вызываете syncdb перед загрузкой светильников. Для этого необходимо переопределить метод _pre_setup (недостаточно поместить код в метод установки). Я использую свою собственную версию TestCase, которая позволяет мне добавлять приложения с тестовыми моделями. Он определяется как следует:

from django.conf import settings
from django.core.management import call_command
from django.db.models import loading
from django import test

class TestCase(test.TestCase):
    apps = ()

    def _pre_setup(self):
        # Add the models to the db.
        self._original_installed_apps = list(settings.INSTALLED_APPS)
        for app in self.apps:
            settings.INSTALLED_APPS.append(app)
        loading.cache.loaded = False
        call_command('syncdb', interactive=False, verbosity=0)
        # Call the original method that does the fixtures etc.
        super(TestCase, self)._pre_setup()

    def _post_teardown(self):
        # Call the original method.
        super(TestCase, self)._post_teardown()
        # Restore the settings.
        settings.INSTALLED_APPS = self._original_installed_apps
        loading.cache.loaded = False

это решение работает только для более ранних версий django (ранее 1.7). Вы можете легко проверить свою версию:

import django
django.VERSION < (1, 7)

оригинальный ответ:

Это довольно странно, но форма меня работает очень простой шаблон:

  1. добавить tests.py к приложению, которое вы собираетесь проверить,
  2. в этом файле просто определите тестовые модели,
  3. ниже поместите свой тестовый код (определение doctest или TestCase),

ниже я поместите некоторый код, который определяет модель статьи, которая нужна только для тестов (она существует в someapp/tests.py и я могу проверить это только с помощью: ./manage.py проверьте someapp ):

class Article(models.Model):
    title = models.CharField(max_length=128)
    description = models.TextField()
    document = DocumentTextField(template=lambda i: i.description)

    def __unicode__(self):
        return self.title

__test__ = {"doctest": """
#smuggling model for tests
>>> from .tests import Article

#testing data
>>> by_two = Article.objects.create(title="divisible by two", description="two four six eight")
>>> by_three = Article.objects.create(title="divisible by three", description="three six nine")
>>> by_four = Article.objects.create(title="divisible by four", description="four four eight")

>>> Article.objects.all().search(document='four')
[<Article: divisible by two>, <Article: divisible by four>]
>>> Article.objects.all().search(document='three')
[<Article: divisible by three>]
"""}

модульные тесты также работают с таким определением модели.

Я выбрал несколько иной, хотя и более сопряженный, подход к динамическому созданию моделей только для тестирования.

Я держу все мои тесты в tests подкаталог, который живет в моем files приложение. Элемент на tests подкаталог содержит мои тестовые модели. Связанная часть приходит сюда, где мне нужно добавить следующее К моему settings.py file:

# check if we are testing right now
TESTING = 'test' in sys.argv

if TESTING:
    # add test packages that have models
    INSTALLED_APPS += ['files.tests',]

Я также установил db_table в моей тестовой модели, потому что в противном случае Django создал бы таблицу с именем tests_<model_name>, что может привести к конфликту с другими тестовыми моделями в другом приложении. Вот моя тестовая модель:

class Recipe(models.Model):

    '''Test-only model to test out thumbnail registration.'''

    dish_image = models.ImageField(upload_to='recipes/')

    class Meta:
        db_table = 'files_tests_recipe'

цитирую связанный ответ:

Если вы хотите, чтобы модели определялись только для тестирования, то вы должны проверить билет Джанго 7835# в частности комментарий #24 часть из которых приводится ниже:

по-видимому, вы можете просто определить модели непосредственно в вашем tests.py. Syncdb никогда не импортирует tests.py, так что эти модели не будут синхронизированы с нормальная БД, но они будут синхронизированы с тестом базы данных, и может быть используется в тестах.

Я решение что я использую в моих проектах. Может быть, это кому-то помогает.

pip install django-fake-model

два простых шага для создания поддельной модели:

1) Определите модель в любом файле (я обычно определяю модель в тестовом файле рядом с тестовым случаем)

from django_fake_model import models as f


class MyFakeModel(f.FakeModel):

    name = models.CharField(max_length=100)

2) Добавить декоратор @MyFakeModel.fake_me на TestCase или для проверки функции.

class MyTest(TestCase):

    @MyFakeModel.fake_me
    def test_create_model(self):
        MyFakeModel.objects.create(name='123')
        model = MyFakeModel.objects.get(name='123')
        self.assertEqual(model.name, '123')

этот декоратор создает таблицу в вашей базе данных перед каждым тестом и удаляет таблицу после испытания.

и создать/удалить таблицы вручную: MyFakeModel.create_table()/MyFakeModel.delete_table()

я придумал способ для тестовых моделей для django 1.7+.

основная идея заключается в том, чтобы сделать ваш tests приложение, и добавить свой tests до INSTALLED_APPS.

вот пример:

$ ls common
__init__.py   admin.py      apps.py       fixtures      models.py     pagination.py tests         validators.py views.py

$ ls common/tests
__init__.py        apps.py            models.py          serializers.py     test_filter.py     test_pagination.py test_validators.py views.py

а у меня по-другому settings для различных целей (ref:разделение файла настроек), а именно:

  • settings/default.py: файл настройки базы
  • settings/production.py: для производства
  • settings/development.py: для развитие
  • settings/testing.py: для тестирования.

и settings/testing.py, вы можете изменить INSTALLED_APPS:

settings/testing.py:

from default import *

DEBUG = True

INSTALLED_APPS += ['common', 'common.tests']

и убедитесь, что вы установили правильную метку для вашего приложения тестов, а именно,

common/tests/apps.py

from django.apps import AppConfig


class CommonTestsConfig(AppConfig):
    name = 'common.tests'
    label = 'common_tests'

common/tests/__init__.py, настройка правильного AppConfig(ref:Приложения Django).

default_app_config = 'common.tests.apps.CommonTestsConfig'

затем создайте миграцию БД с помощью

python manage.py makemigrations --settings=<your_project_name>.settings.testing tests

наконец, вы можете запустить свой тест с param --settings=<your_project_name>.settings.testing.

если вы используете py.тест, вы даже можете удалить вместе с .

py.test

[pytest]
DJANGO_SETTINGS_MODULE=kungfu.settings.testing

вот шаблон, который я использую для этого.

Я написал этот метод, который я использую на подклассе версии TestCase. Он выглядит следующим образом:

@classmethod
def create_models_from_app(cls, app_name):
    """
    Manually create Models (used only for testing) from the specified string app name.
    Models are loaded from the module "<app_name>.models"
    """
    from django.db import connection, DatabaseError
    from django.db.models.loading import load_app

    app = load_app(app_name)
    from django.core.management import sql
    from django.core.management.color import no_style
    sql = sql.sql_create(app, no_style(), connection)
    cursor = connection.cursor()
    for statement in sql:
        try:
            cursor.execute(statement)
        except DatabaseError, excn:
            logger.debug(excn.message)
            pass

затем я создаю специальный тест-специфичный models.py файл в чем-то вроде myapp/tests/models.py это не входит в INSTALLED_APPS.

в моем методе установки я вызываю create_models_from_app ('myapp.тесты"), и он создает соответствующие таблицы.

единственный "попался" с таким подходом является то, что вы не хочу создавать модели либо времени setUp работает, поэтому я ловлю DatabaseError. Я думаю, что вызов этого метода может идти в верхней части тестового файла, и это будет работать немного лучше.

комбинируя ваши ответы, особенно @slacy'S, Я сделал это:

class TestCase(test.TestCase):
    initiated = False

    @classmethod
    def setUpClass(cls, *args, **kwargs):
        if not TestCase.initiated:
            TestCase.create_models_from_app('myapp.tests')
            TestCase.initiated = True

        super(TestCase, cls).setUpClass(*args, **kwargs)

    @classmethod
    def create_models_from_app(cls, app_name):
        """
        Manually create Models (used only for testing) from the specified string app name.
        Models are loaded from the module "<app_name>.models"
        """
        from django.db import connection, DatabaseError
        from django.db.models.loading import load_app

        app = load_app(app_name)
        from django.core.management import sql
        from django.core.management.color import no_style
        sql = sql.sql_create(app, no_style(), connection)
        cursor = connection.cursor()
        for statement in sql:
            try:
                cursor.execute(statement)
            except DatabaseError, excn:
                logger.debug(excn.message)

при этом вы не пытаетесь создавать таблицы БД более одного раза, и вам не нужно менять свои INSTALLED_APPS.

если вы пишете многоразовое django-приложение,создайте минимальное тестовое приложение для него!

$ django-admin.py startproject test_myapp_project
$ django-admin.py startapp test_myapp

добавить myapp и test_myapp до INSTALLED_APPS, создать свои модели там, и это хорошо, чтобы пойти!

Я прошел через все эти ответы, а также Джанго билета 7835, и я, наконец, пошел на совершенно другой подход. Я хотел, чтобы мое приложение (каким-то образом расширяя queryset.значения () ), чтобы быть в состоянии быть проверены в изоляции; также, Мой пакет включает в себя некоторые модели, и я хотел четкое различие между тестовыми моделями и пакетами.

именно тогда я понял, что было проще добавить очень маленький проект django в пакет! Это также позволяет гораздо более чистое разделение кода IMHO:

там вы можете чисто и без какого-либо взлома определить свои модели, и вы знаете, что они будут созданы, когда вы запустите свои тесты оттуда!

если вы не пишете независимое, многоразовое приложение, которое вы можете все равно идите этим путем: создайте test_myapp приложение, и добавить его в свой INSTALLED_APPS только в отдельном settings_test_myapp.py!

Comments

    Ничего не найдено.