7

我有一个名为“publisher”的 Django 应用程序,它连接到我的 django 项目中的各种信号,当它接收到它们时,它会向 rabbitmq 队列发送一条消息。我想要做的是能够测试我的设置代码是否连接到正确的信号。

我的应用程序结构如下所示:

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

我的 __init__.py 看起来像:

import signals

和我的信号.py:

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

my_interesting_signal.connect(receiver_function)

我考虑过修补接收器函数,并检查在我发送信号时是否调用了模拟:

测试.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”。信号有一个称为“接收器”的内部属性,它是一个双元组列表,其中第二个元素是您注册的函数的弱引用(因此 r[1])。调用弱引用会返回所指对象。

我不得不和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.

模块.py

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

signals.post_save.connect(receiver_function)

测试.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))

这是因为 Djangodjango.test.TestCaseunittest.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 回答