7

「publisher」というDjangoアプリがあり、djangoプロジェクトのさまざまなシグナルに接続し、それらを受信すると、rabbitmqキューにメッセージを送信します。私がやりたいのは、セットアップコードが正しい信号に接続されていることをテストできるようにすることです。

私のアプリの構造は次のようになります。

publisher
    - __init__.py
    - signals.py
    - tests.py

私の__init__。pyは次のようになります:

import signals

と私のsignals.py:

def receiver_function(*args, **kwargs):
    #Does rabbitmq stuff

my_interesting_signal.connect(receiver_function)

レシーバー機能にパッチを適用し、信号を送信したときにモックが呼び出されたことを確認することを考えました。

tests.py:

class SignalsTeste(TestCase):

    def_test_connection(self):

        with patch('publisher.signals.receiver_function') as receiver_mock:
            my_interesting_signal.application_created.send(None)
            self.assertEquals(receiver_mock.call_count, 1)

ただし、シグナルモジュールがインポートされ、テストが実行される前にシグナル接続が行われるため、関数にパッチが適用される前に接続が行われるため、このアプローチは機能しません......

誰かが別の戦略を提案できますか?

4

4 に答える 4

17

私はあなたが説明するのと同じあざける問題に遭遇しました。私の解決策は、Djangoのシグナルレジストリにアクセスして、私の関数が正しいシグナルで登録されたことを表明することです。

これが私のテストです:

def test_signal_registry(self):
    from foo.models import bar_func  # The function I want to register.
    from django.db.models import signals
    registered_functions = [r[1]() for r in signals.pre_delete.receivers]
    self.assertIn(bar_func, registered_functions)

そのリスト内包表記についての少しの説明:

「pre_delete」は、この場合気にしたdjango.dispatch.dispatcher.Signalのインスタンスです。例では、独自の「my_interesting_signal」を使用します。シグナルには、2つのタプルのリストである「レシーバー」と呼ばれる内部プロパティがあります。2番目の要素は、登録する関数へのweakrefです(したがって、r [1])。weakrefを呼び出すと、指示対象が返されます。

私はその多くを理解するためにweakrefsで遊んでいなければなりませんでした:

import weakref
def foo():
    pass
w = weakref.ref(foo)
w() == foo

お役に立てれば。

于 2012-11-09T19:06:59.690 に答える
5

信号が接続されているかどうかをテストする方法は、信号を切断し、このアクションの結果を確認することです。信号が切断された場合、または切断されなかった場合、呼び出し<some_signal>.disconnect(...)は戻ります。TrueFalse

たとえば、post_save信号がに接続されていることをテストしたいとしますreceiver_function

modules.py

def receiver_function(*args, **kwargs):
    pass

signals.post_save.connect(receiver_function)

tests.py

class SignalsTest(TestCase):
    def test_connection(self):
        result = signals.post_save.disconnect(receiver_function)

        self.assertTrue(result)

の呼び出しでは、 call(、)disconnectと同じ引数を使用する必要がありますconnectsenderdispatch_uid

テスト後に信号を再接続する必要があります。そうでない場合、信号は切断されたままになります

于 2016-05-15T13:10:03.690 に答える
1

あなたが言うように、あなたがレシーバーを含むファイルから何かをモックまたはインポートするならば、あなたはそれを自動的に接続するので、これはかなりトリッキーです。これは、問題のテストファイルだけでなく、テストスイート全体に適用されます。使用できるスニペットは次のとおりですが、レシーバーファイルへのインポートの回避に関するコメントに従うように訓練する必要があります。

from django.test import TestCase

class ReceiverConnectionTestCase(TestCase):
    """TestCase that allows asserting that a given receiver is connected
    to a signal.

    Important: this will work correctly providing you:
        1. Do not import or patch anything in the module containing the receiver
           in any django.test.TestCase.
        2. Do not import (except in the context of a method) the module
           containing the receiver in any test module.

    This is because as soon as you import/patch, the receiver will be connected
    by your test and will be connected for the entire test suite run.

    If you want to test the behaviour of the receiver, you may do this
    providing it is a unittest.TestCase, and there is no import from the
    receiver module in that test module.

    Usage:

        # myapp/receivers.py
        from django.dispatch import receiver
        from apples.signals import apple_eaten
        from apples.models import Apple

        @receiver(apple_eaten, sender=Apple)
        def my_receiver(sender, **kwargs):
            pass


        # tests/integration_tests.py
        from apples.signals import apple_eaten
        from apples.models import Apple

        class TestMyReceiverConnection(ReceiverConnectionTestCase):
            def test_connection(self):
                self.assert_receiver_is_connected(
                    'myapp.receivers.my_receiver',
                    signal=apple_eaten, sender=Apple)

    """
    def assert_receiver_is_connected(self, receiver_string, signal, sender):
        receivers = signal._live_receivers(sender)
        receiver_strings = [
            "{}.{}".format(r.__module__, r.__name__) for r in receivers]
        if receiver_string not in receiver_strings:
            raise AssertionError(
                '{} is not connected to signal.'.format(receiver_string))

これは、Djangoがのdjango.test.TestCase前に実行されるために機能しunittest.TestCaseます。

于 2017-03-03T13:20:40.413 に答える
0

また、必要なすべての信号が接続されていることを確認するという問題にも直面しました。コメントありがとうございます。その結果、イントロスペクションに基づくSignalConnectionTestCaseが利用可能になり(@ frank-tによって提案されたように)、組み込みシグナルの場合、初期問題の解決は次のように実行できます。

class SignalsTest(SignalConnectionTestCase):
    expected_post_save = ['publisher.signals.receiver_function']

独自のシグナルの場合はverify、テストメソッドで関数を呼び出すだけです。

于 2021-03-25T00:50:04.990 に答える