Djangoアプリにはsettings
属性が次の形式で必要です:
RELATED_MODELS = ('appname1.modelname1.attribute1',
'appname1.modelname2.attribute2',
'appname2.modelname3.attribute3', ...)
次に、定義されたattributeN
に応じて、post_save信号をフックして、他のいくつかの固定モデルを更新します。
この動作をテストしたいのですが、このアプリがプロジェクトで唯一のアプリであってもテストは機能します(独自の依存関係を除いて、他のラッパーアプリをインストールする必要はありません)。テストデータベース専用のモックモデルを作成してアタッチ/登録/アクティブ化するにはどうすればよいですか? (またはそれはまったく可能ですか?)
テストフィクスチャを使用できるようにするソリューションは素晴らしいでしょう。
アプリのtests/
サブディレクトリに(tests.py
ファイルではなく)テストを配置し、テスト専用モデルにtests/models.py
を含めることができます。
次に、tests/
にINSTALLED_APPS
"アプリ"を含むテスト実行スクリプト( 例 )を提供します。 (これは、INSTALLED_APPS
にテストアプリがない実際のプロジェクトからアプリのテストを実行する場合は機能しませんが、プロジェクトから再利用可能なアプリのテストを実行することはめったになく、Django 1.6+はデフォルトではありません。)
([〜#〜]注[〜#〜]:以下で説明する代替動的メソッドは、Django 1.1+でのみ機能しますサブクラスTransactionTestCase
-テストの速度が大幅に低下します-Django 1.7+でまったく機能しなくなります。これは歴史的な目的でのみ残されています。使用しないでください。)
テストの最初(つまり、setUpメソッド内、またはdoctestのセットの最初)で、"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.test.TestCaseでは機能しません。 Django.test.TestCaseを使用する場合は、フィクスチャがロードされる前に必ずsyncdbを呼び出す必要があります。これには、_pre_setupメソッドをオーバーライドする必要があります(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)
元の応答:
それはかなり奇妙ですが、私が非常に単純なパターンで機能するフォーム:
以下に、テストにのみ必要なArticleモデルを定義するコードをいくつか置きます(someapp/tests.pyに存在し、次のようにテストできます./ manage.py test 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>]
"""}
ユニットテストは、このようなモデル定義でも機能します。
プロジェクトで使用する solution を共有しました。多分それは誰かを助ける。
_pip install Django-fake-model
_
偽のモデルを作成するための2つの簡単なステップ:
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')
_
このデコレータは、各テストの前にデータベースにテーブルを作成し、テスト後にテーブルを削除します。
また、手動でcreate/deletedelete:MyFakeModel.create_table()
/MyFakeModel.delete_table()
関連する答え からの引用:
テスト専用にモデルを定義したい場合は、 Djangoチケット#7835 をチェックアウトする必要があります。特に コメント#24 の一部を以下に示します。
どうやら、tests.pyで直接モデルを直接定義できます。 Syncdbはtests.pyをインポートしないため、これらのモデルは通常のdbに同期されませんが、テストデータベースに同期され、テストで使用できます。
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 Applications )を設定します。
default_app_config = 'common.tests.apps.CommonTestsConfig'
次に、db移行を生成します
python manage.py makemigrations --settings=<your_project_name>.settings.testing tests
最後に、param --settings=<your_project_name>.settings.testing
を使用してテストを実行できます。
Py.testを使用する場合は、Djangoのpytest.ini
とともにmanage.py
ファイルをドロップすることもできます。
py.test
[pytest]
Django_SETTINGS_MODULE=kungfu.settings.testing
テストのためだけにモデルを動的に作成するために、結合度は高くなりますが、少し異なるアプローチを選択しました。
すべてのテストをtests
アプリにあるfiles
サブディレクトリに保存します。 tests
サブディレクトリのmodels.py
ファイルには、テスト専用モデルが含まれています。結合された部分がここに入ります。ここで、以下をsettings.py
ファイルに追加する必要があります。
# 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'
これが私がこれを行うために使用しているパターンです。
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
次に、INSTALLED_APPSに含まれていないmyapp/tests/models.py
などの特別なテスト固有のmodels.pyファイルを作成します。
私のsetUpメソッドでは、create_models_from_app( 'myapp.tests')を呼び出し、適切なテーブルを作成します。
このアプローチの唯一の「落とし穴」は、setUp
を実行するたびに実際にモデルを作成したくないということです。そのため、私はDatabaseErrorをキャッチします。このメソッドの呼び出しはテストファイルの先頭に移動する可能性があり、それが少しうまく機能すると思います。
あなたの答え、特に@slacyのものを組み合わせて、私はこれをしました:
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)
これにより、dbテーブルを複数回作成する必要がなくなり、INSTALLED_APPSを変更する必要がなくなります。
再利用可能なDjangoアプリを作成している場合は、そのための最小限のテスト専用アプリを作成します!
$ Django-admin.py startproject test_myapp_project
$ Django-admin.py startapp test_myapp
myapp
とtest_myapp
の両方をINSTALLED_APPS
に追加し、そこでモデルを作成したら、準備完了です。
私はこれらすべての回答とDjango ticket 7835 を通過しました。そして、ついに私はまったく異なるアプローチを取りました。私は自分のアプリ(何らかの形でqueryset.values ())分離してテストできるようにするために、また、私のパッケージにはいくつかのモデルが含まれており、テストモデルとパッケージのモデルを明確に区別する必要がありました。
そのとき、パッケージに非常に小さなDjangoプロジェクトを追加する方が簡単であることがわかりました!これにより、コードをよりきれいに分離することができます。
そこではハックすることなくきれいにモデルを定義でき、そこからテストを実行するとモデルが作成されることがわかります!
独立した再利用可能なアプリを作成していない場合でも、この方法で作成できます。test_myapp
アプリを作成し、別のsettings_test_myapp.py
でのみINSTALLED_APPSに追加してください。
誰かがすでに言及しました Django ticket#7835 ですが、より最近のバージョンのDjangoに対してより有望に見えるより最近の返信があるようです。特に #42 は、異なるTestRunner
を提案します:
from importlib.util import find_spec
import unittest
from Django.apps import apps
from Django.conf import settings
from Django.test.runner import DiscoverRunner
class TestLoader(unittest.TestLoader):
""" Loader that reports all successful loads to a runner """
def __init__(self, *args, runner, **kwargs):
self.runner = runner
super().__init__(*args, **kwargs)
def loadTestsFromModule(self, module, pattern=None):
suite = super().loadTestsFromModule(module, pattern)
if suite.countTestCases():
self.runner.register_test_module(module)
return suite
class RunnerWithTestModels(DiscoverRunner):
""" Test Runner that will add any test packages with a 'models' module to INSTALLED_APPS.
Allows test only models to be defined within any package that contains tests.
All test models should be set with app_label = 'tests'
"""
def __init__(self, *args, **kwargs):
self.test_packages = set()
self.test_loader = TestLoader(runner=self)
super().__init__(*args, **kwargs)
def register_test_module(self, module):
self.test_packages.add(module.__package__)
def setup_databases(self, **kwargs):
# Look for test models
test_apps = set()
for package in self.test_packages:
if find_spec('.models', package):
test_apps.add(package)
# Add test apps with models to INSTALLED_APPS that aren't already there
new_installed = settings.INSTALLED_APPS + Tuple(ta for ta in test_apps if ta not in settings.INSTALLED_APPS)
apps.set_installed_apps(new_installed)
return super().setup_databases(**kwargs)