Djangoのさまざまな設定でユニットテストを行う方法


116

単体テストのDjango設定をオーバーライドする簡単なメカニズムはありますか?特定の数の最新オブジェクトを返すモデルの1つにマネージャーがいます。返されるオブジェクトの数は、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()単にデフォルト設定は存在しないはずを提供するために使用されます。


@Anto-理由を説明したり、より良い答えを提供したりできますか?
ユーザー

それはその間に変わりました。前者はこれを受け入れた;)
Anto

回答:


163

編集:この回答は、少数特定のテストの設定を変更する場合に適用されます。

Django 1.4以降、テスト中に設定を上書きする方法があります:https : //docs.djangoproject.com/en/dev/topics/testing/tools/#overriding-settings

TestCaseにはself.settingsコンテキストマネージャーがあり、テストメソッドまたはTestCaseサブクラス全体に適用できる@override_settingsデコレーターもあります。

これらの機能はDjango 1.3にはまだありません。

すべてのテストの設定を変更する場合は、テスト用の個別の設定ファイルを作成する必要があります。これにより、メイン設定ファイルから設定を読み込んで上書きできます。他の答えには、これに対するいくつかの良いアプローチがあります。私はhspanderdmitriiの両方アプローチで成功したバリエーションを見てきました。


4
これが今Django 1.4以降でこれを行う最良の方法だと思います
Michael

後でテスト内からその設定にどのようにアクセスしますか?私が見つけたベストはのようなものですがself.settings().wrapped.MEDIA_ROOT、それはかなりひどいです。
mlissner 2014年

2
:ジャンゴの新しいバージョンは、このために、特定のコンテキストマネージャ持っdocs.djangoproject.com/en/1.8/topics/testing/tools/...
Akhorus

私のお気に入り:(@modify_settings(MIDDLEWARE_CLASSES=...この回答をありがとう)
guettli 2016年

44

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値を変更する他の方法について知りたいのですが。テストルーチンによってその「何か」がトリガーされた場合、サルのパッチをどれだけ適用しても、テスト自体の真実性を無効にすることなくテストを保存できるかどうかはわかりません。


あなたの例はうまくいきました。これは、単体テストの範囲と、テストファイルの設定がコールスタックを通じてどのように伝播するかという点で、目を見張るものでした。
ソビエト2009年

これは動作しませんsettings.TEMPLATE_LOADERS...したがって、これは少なくとも一般的な方法ではありません。設定やDjangoがリロードされたり、このトリックで何かが読み込まれることはありません。
Ciantic

1
これはDjango 1.4より古いバージョンの良い例です。1.4 以上の回答の場合、stackoverflow.com
a / 6415129/190127

docs.djangoproject.com/en/dev/topics/testing/tools/…を使用してください。 このようにsetUpとtearDownを使用してパッチを適用することは、必要以上に冗長で実際に壊れやすいテストを作成するための優れた方法です。このようなパッチを適用する必要がある場合は、flexmockなどを使用してください。
ファジーワッフル2014年

"Djangoのテストケースはシングルスレッドで実行されるため":Django 1.9ではもはやそうではありません。
Wtower

22

ランタイムで設定構成を上書きすることで解決する場合がありますが、私の意見では、テスト用に別のファイルを作成する必要があります。これにより、テストのための構成が大幅に節約され、これにより、(ステージングデータベースのクリーンアップなど)不可逆的な処理が行われることがなくなります。

テストファイルが 'my_project / test_settings.py'に存在するとします。

settings = 'my_project.test_settings' if 'test' in sys.argv else 'my_project.settings'

あなたのmanage.pyで。これにより、実行時にpython manage.py testtest_settingsのみが使用されるようになります。pytestなどの他のテストクライアントを使用している場合は、これをpytest.iniに簡単に追加できます。


2
これは私にとって良い解決策だと思います。キャッシュを使用しているテストやコードが多すぎます。設定をひとつひとつ上書きするのは難しいです。2つの構成ファイルを作成し、どちらを使用するかを決定します。MicroPyramidの回答も利用できますが、設定パラメーターを一度追加し忘れた場合は危険です。
ramwin 2018年

22

--settingsテストの実行時にオプションを渡すことができます

python manage.py test --settings=mysite.settings_local

settings.baseの拡張であるsettings.devにあるアプリを見つけるのをやめました
holms

4
一度設定パラメータを追加し忘れると危険だと思います。
ramwin 2018年

20

更新:以下のソリューションは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()

これは本当にクールなソリューションです。何らかの理由で、ユニットテストで設定が適切に機能しませんでした。共有してくれてありがとうございます。
Tomas

このコードを使用していますが、問題のテストが失敗しても設定が元に戻されないため、カスケードテストの失敗に問題がありました。これに対処するためyieldに、finallyブロックの中に関数の最後の部分が含まれるように、ステートメントの周りにtry / finallyを追加しました。これにより、設定は常に元に戻ります。
ダスティンRasener

後世のために答えを編集します。私はこれが正しく行われていることを願っています!:)
Dustin Rasener

11

@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 = {}

そして--settings、@ MicroPyramid answerのようにオプションを指定するか、DJANGO_SETTINGS_MODULE環境変数を指定して、テストを実行できます。

export DJANGO_SETTINGS_MODULE=settings.test
python manage.py test 

こんにちは 。Dmitrii、あなたの答えに感謝します。この答えと同じケースがありますが、アプリがどのように認識されるか、私たちがいる環境(テストまたは本番環境)についてさらにガイダンスを得たい、私のブランチを見て、チェックアウトしてください私のリポジトリgithub.com/andela/ah-backend-iroquois/tree/develop/authors、そのロジックをどのように処理するのですか?
Lutaaya Huzaifah Idris '30

nosetestsを使用してテストを実行しているので、開発環境で
Lutaaya Huzaifah Idris '30

3

いくつかのdoctestを修正しようとしたときにこれが見つかりました...完全を期すために、doctestを使用するときに設定を変更する場合は、他のものをインポートする前に行う必要があることを述べておきます...

>>> from django.conf import settings

>>> settings.SOME_SETTING = 20

>>> # Your other imports
>>> from django.core.paginator import Paginator
>>> # etc

3

以下のためのpytestユーザー。

最大の問題は:

  • override_settings pytestでは動作しません。
  • Djangoをサブクラス化TestCaseすると機能しますが、pytestフィクスチャは使用できません。

解決策は、ここにsettings記載されているフィクスチャを使用することです

def test_with_specific_settings(settings):
    settings.DEBUG = False
    settings.MIDDLEWARE = []
    ..

複数のフィールドを更新する必要がある場合

def override_settings(settings, kwargs):
    for k, v in kwargs.items():
        setattr(settings, k, v)


new_settings = dict(
    DEBUG=True,
    INSTALLED_APPS=[],
)


def test_with_specific_settings(settings):
    override_settings(settings, new_settings)

2

単一のテスト機能でも設定を上書きできます。

from django.test import TestCase, override_settings

class SomeTestCase(TestCase):

    @override_settings(SOME_SETTING="some_value")
    def test_some_function():
        

または、クラスの各関数の設定をオーバーライドできます。

@override_settings(SOME_SETTING="some_value")
class SomeTestCase(TestCase):

    def test_some_function():
        

1

私は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

1

このようにして、テストの設定を上書きできます。

from django.test import TestCase, override_settings

test_settings = override_settings(
    DEFAULT_FILE_STORAGE='django.core.files.storage.FileSystemStorage',
    PASSWORD_HASHERS=(
        'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher',
    )
)


@test_settings
class SomeTestCase(TestCase):
    """Your test cases in this class"""

また、これらの同じ設定が別のファイルで必要な場合は、直接インポートすることができますtest_settings


0

サブディレクトリ(pythonパッケージ)に複数のテストファイルを配置している場合、sys.argvに 'test'文字列が存在する条件に基づいて、これらすべてのファイルの設定を上書きできます。

app
  tests
    __init__.py
    test_forms.py
    test_models.py

__init__.py:

import sys
from project import settings

if 'test' in sys.argv:
    NEW_SETTINGS = {
        'setting_name': value,
        'another_setting_name': another_value
    }
    settings.__dict__.update(NEW_SETTINGS)

最善のアプローチではありません。CeleryブローカーをRedisからMemoryに変更するために使用しました。


0

新しいsettings_test.pyファイルを作成しました。これは、settings.pyファイルからすべてをインポートし、テストのために異なるものを変更します。私の場合、テストするときに別のクラウドストレージバケットを使用したいと思いました。 ここに画像の説明を入力してください

settings_test.py:

from project1.settings import *
import os

CLOUD_STORAGE_BUCKET = 'bucket_name_for_testing'

manage.py:

def main():

    # use seperate settings.py for tests
    if 'test' in sys.argv:
        print('using settings_test.py')
        os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project1.settings_test')
    else:
        os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project1.settings')

    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.