Django単体テストの設定をオーバーライドする簡単なメカニズムはありますか?特定の数の最新オブジェクトを返すモデルのマネージャーがあります。それが返すオブジェクトの数は定義されていますNUM_LATEST設定によって。
これにより、誰かが設定を変更した場合にテストが失敗する可能性があります。 setUp()
の設定をオーバーライドし、その後tearDown()
でそれらを復元するにはどうすればよいですか?それが不可能な場合、メソッドにパッチを適用したり、設定を模擬したりする方法はありますか?
編集:ここに私のマネージャーコードがあります:
_class LatestManager(models.Manager):
"""
Returns a specific number of the most recent public Articles as defined by
the NEWS_LATEST_MAX setting.
"""
def get_query_set(self):
num_latest = getattr(settings, 'NEWS_NUM_LATEST', 10)
return super(LatestManager, self).get_query_set().filter(is_public=True)[:num_latest]
_
マネージャーは_settings.NEWS_LATEST_MAX
_を使用してクエリセットをスライスします。 getattr()
は、設定が存在しない場合にデフォルトを提供するために単に使用されます。
編集:この回答は、small数のspecificテストの設定を変更する場合に適用されます。
Django 1.4なので、テスト中に設定をオーバーライドする方法があります。 https://docs.djangoproject.com/en/dev/topics/testing/tools/#overriding-settings
TestCaseにはself.settingsコンテキストマネージャーがあり、テストメソッドまたはTestCaseサブクラス全体に適用できる@override_settingsデコレーターもあります。
これらの機能は、Django 1.3ではまだ存在していませんでした。
テストのallの設定を変更する場合は、テスト用に別の設定ファイルを作成します。これにより、メインの設定ファイルから設定を読み込んで上書きできます。他の回答では、これに対するいくつかの良いアプローチがあります。 hspander's と dmitrii's の両方のアプローチで成功したバリエーションを見てきました。
インスタンスプロパティの設定や読み取りなど、UnitTest
サブクラスに対しては何でもできます。
from Django.conf import settings
class MyTest(unittest.TestCase):
def setUp(self):
self.old_setting = settings.NUM_LATEST
settings.NUM_LATEST = 5 # value tested against in the TestCase
def tearDown(self):
settings.NUM_LATEST = self.old_setting
Djangoテストケースはシングルスレッドで実行されるため、NUM_LATEST値を変更する他に何が必要なのでしょうか?テストルーチンによってその「何か」がトリガーされる場合、私はモンキーパッチを適用しても、テスト自体の信頼性を無効にすることなくテストが保存されるかどうかはわかりません。
Update:以下のソリューションは、Django 1.3.x以前でのみ必要です。> 1.4については、 slinkpの答え 。
テストで設定を頻繁に変更し、Python≥2.5を使用する場合、これも便利です。
from contextlib import contextmanager
class SettingDoesNotExist:
pass
@contextmanager
def patch_settings(**kwargs):
from Django.conf import settings
old_settings = []
for key, new_value in kwargs.items():
old_value = getattr(settings, key, SettingDoesNotExist)
old_settings.append((key, old_value))
setattr(settings, key, new_value)
yield
for key, old_value in old_settings:
if old_value is SettingDoesNotExist:
delattr(settings, key)
else:
setattr(settings, key, old_value)
その後、次のことができます。
with patch_settings(MY_SETTING='my value', OTHER_SETTING='other value'):
do_my_tests()
テストの実行時に--settings
オプションを渡すことができます
python manage.py test --settings=mysite.settings_local
実行時の設定構成のオーバーライドが役立つ場合がありますが、私の意見では、テスト用に別のファイルを作成する必要があります。これにより、テストのための構成が大幅に節約され、これにより、最終的に不可逆的なこと(ステージングデータベースのクリーニングなど)を行うことがなくなります。
テストファイルが「my_project/test_settings.py」に存在する場合、追加します
settings = 'my_project.test_settings' if 'test' in sys.argv else 'my_project.settings'
manage.pyで。これにより、python manage.py test
test_settingsのみを使用します。 pytestのような他のテストクライアントを使用している場合、これをpytest.iniに簡単に追加できます。
@override_settings
は、実稼働環境とテスト環境の構成に大きな違いがない場合に最適です。
それ以外の場合は、異なる設定ファイルを用意した方が良いでしょう。この場合、プロジェクトは次のようになります。
your_project
your_app
...
settings
__init__.py
base.py
dev.py
test.py
production.py
manage.py
そのため、ほとんどの設定をbase.py
に設定し、他のファイルではそこからすべてをインポートして、いくつかのオプションをオーバーライドする必要があります。 test.py
ファイルは次のようになります。
from .base import *
DEBUG = False
DATABASES = {
'default': {
'ENGINE': 'Django.db.backends.sqlite3',
'NAME': 'app_db_test'
}
}
PASSWORD_HASHERS = (
'Django.contrib.auth.hashers.MD5PasswordHasher',
)
LOGGING = {}
そして、@ MicroPyramid answerのように--settings
オプションを指定するか、Django_SETTINGS_MODULE
環境変数を指定してからテストを実行する必要があります。
export Django_SETTINGS_MODULE=settings.test
python manage.py test
いくつかのdoctestを修正しようとしているときにこれを見つけました。
>>> from Django.conf import settings
>>> settings.SOME_SETTING = 20
>>> # Your other imports
>>> from Django.core.paginator import Paginator
>>> # etc
Pytestを使用しています。
私はこれを次の方法で解決できました。
import Django
import app.setting
import modules.that.use.setting
# do some stuff with default setting
setting.VALUE = "some value"
Django.setup()
import importlib
importlib.reload(app.settings)
importlib.reload(modules.that.use.setting)
# do some stuff with settings new value