web-dev-qa-db-ja.com

Djangoテストでシグナルを無効にしたい

そのため、アプリ間で送信されるさまざまなシグナルとハンドラーがあります。ただし、テストを実行するとき/「テストモード」に入るときは、これらのハンドラーを無効にします。

テストモードでシグナル/ハンドラーを無効にするDjango固有の方法はありますか?非常に簡単な方法(if TESTING句内にハンドラーを含める)を考えることができますが、Djangoに組み込まれているより良い方法があるかどうか疑問に思っていました...

27
user2564502

いいえ、ありません。ただし、条件付き接続は簡単に作成できます。

import sys

if not 'test' in sys.argv:
    signal.connect(listener, sender=FooModel)
6
Germano

一連のテストケースのシグナルを無効にすることを検討しているときにこの質問を見つけました。ゲルマノの答えは私を解決策に導きましたが、それは反対のアプローチを取るので、私はそれを追加すると思いました。

テストクラスの場合:

class MyTest(TestCase):
    def setUp(self):
        # do some setup
        signal.disconnect(listener, sender=FooModel)

シグナルを追加するために決定コードを追加する代わりに、テストの時点でそれを無効にしました。これは、私にとってより良い解決策のように感じます(テストは、テストの周りのコードではなく、コードの周りに書く必要があるため)。うまくいけば、同じボートの誰かに役立つでしょう!

編集:これを書いているので、私はテストのために信号を無効にする別の方法を紹介されました。これには factory_boy パッケージ(v2.4.0 +)が必要です。これは、Djangoでのテストを簡素化するのに非常に便利です。あなたは本当に選択のために甘やかされています:

import factory
from Django.db.models import signals

class MyTest(TestCase):
    @factory.Django.mute_signals(signals.pre_save, signals.post_save)
    def test_something(self):

Upsのおかげで警告:ファクトリ内およびオブジェクトの作成時にシグナルをミュートしますが、明示的なsave()を作成する場合はテスト内でそれ以上ミュートしません-シグナルはそこでミュート解除されます。これが問題である場合は、setUpで単純な切断を使用するのがおそらく最善の方法です。

82
krischan

FactoryBoyを使用したくない場合に、テストで特定のシグナルを無効にする方法をインポートした完全な例を次に示します。

from Django.db.models import signals
from myapp.models import MyModel

class MyTest(TestCase):

    def test_no_signal(self):
        signals.post_save.disconnect(sender=MyModel, dispatch_uid="my_id")

        ... after this point, the signal is disabled ...

これはレシーバーと一致する必要があります。この例はこのレシーバーと一致します。

@receiver(post_save, sender=MyModel, dispatch_uid="my_id")

dispatch_uidを指定せずにシグナルを無効にしようとしましたが、機能しませんでした。

13
mrmuggles

同様の問題が発生し、signals.post_save.disconnect()を使用してシグナルを正常に切断できませんでした。見つかりました this 指定されたテストとシグナルの_SUSPEND_SIGNALS_設定をオーバーライドするデコレータを作成する代替アプローチ。同じボートに乗っている人なら誰にでも役立つかもしれません。

まず、デコレータを作成します。

_import functools

from Django.conf import settings
from Django.dispatch import receiver

def suspendingreceiver(signal, **decorator_kwargs):
    def our_wrapper(func):
        @receiver(signal, **decorator_kwargs)
        @functools.wraps(func)
        def fake_receiver(sender, **kwargs):
            if settings.SUSPEND_SIGNALS:
                return
            return func(sender, **kwargs)
        return fake_receiver
    return our_wrapper
_

シグナルの通常の_@receiver_デコレータを新しいものに置き換えます。

_@suspendingreceiver(post_save, sender=MyModel)
def mymodel_post_save(sender, **kwargs):
    work()
_

TestCaseでDjangoの override_settings() を使用します。

_@override_settings(SUSPEND_SIGNALS=True)
class MyTestCase(TestCase):
    def test_method(self):
        Model.objects.create()  # post_save_receiver won't execute
_

ブログを書いたJoshSmeatonに感謝します。

1
qwertysmack

ドキュメントで推奨されているAppConfig.readyの信号にレシーバーを接続する場合は、 https://docs.djangoproject.com/en/2.2/topics/signals/#connecting-receiver-functions を参照してください。 =、他の信号受信機でのテスト用に代替のAppConfigを作成できます。

0
Vlastimil Zíma