Django:AppConfig就绪函数中使用的覆盖设置

时间:2015-06-30 20:44:43

标签: python django unit-testing startup

我们正在尝试编写AppConfig.ready函数行为的自动化测试,我们将其用作Django应用程序加载时运行代码的初始化挂钩。我们的ready方法实现使用了我们需要在测试中覆盖的Django设置,我们自然会尝试使用override_settings装饰器来实现此目的。

但是有一个障碍 - 当测试运行时,在执行ready功能时,设置覆盖没有被踢(它仍然使用来自{{1}的原始值})。是否有一种方法可以在调用settings.py函数时以覆盖方式应用的方式覆盖设置?

用于演示此行为的一些代码:

settings.py

ready

dummy_app / __初始化__。PY

MY_SETTING = 'original value'

dummy_app / apps.py

default_app_config = 'dummy_app.apps.DummyAppConfig'

dummy_app / tests.py

from django.apps import AppConfig
from django.conf import settings


class DummyAppConfig(AppConfig):
    name = 'dummy_app'

    def ready(self):
        print('settings.MY_SETTING in app config ready function: {0}'.format(settings.MY_SETTING))

输出

from django.conf import settings
from django.test import TestCase
from django.test.utils import override_settings


@override_settings(MY_SETTING='overridden value')
@override_settings(INSTALLED_APPS=('dummy_app',))
class AppConfigTests(TestCase):

    def test_to_see_where_overridden_settings_value_is_available(self):
        print('settings.MY_SETTING in test function: '.format(settings.MY_SETTING))
        self.fail('Trigger test output')

请务必注意,我们只想为声明====================================================================== FAIL: test_to_see_where_overridden_settings_value_is_available (dummy_app.tests.AppConfigTests) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/labminds/venv/labos/src/dom-base/dummy_app/tests.py", line 12, in test_to_see_where_overridden_settings_value_is_available self.fail('Trigger test output') AssertionError: Trigger test output -------------------- >> begin captured stdout << --------------------- settings.MY_SETTING in app config ready function: original value settings.MY_SETTING in test function: overridden value --------------------- >> end captured stdout << ---------------------- 行为的测试覆盖此设置,这就是为什么我们不考虑更改ready中的设置,或使用此文件的单独版本,仅用于运行我们的自动化测试。

已经考虑过一个选项 - 我们可以在测试中简单地初始化settings.py类,调用AppConfig并测试行为(此时设置将被装饰器覆盖)。但是,我们更喜欢将其作为集成测试运行,并依赖Django的自然行为为我们调用函数 - 这对我们来说是关键功能,我们希望确保在Django初始化时测试失败行为改变。

2 个答案:

答案 0 :(得分:2)

一些想法(需要不同的努力和自动保证):

  • 不要进行集成测试,并在升级Django版本和/或依赖单一手动测试之前依赖于阅读发行说明/提交
  • 假设测试阶段部署 - prod部署管道,单独测试特殊情况并将集成检查添加为部署冒烟测试(例如:通过管理命令或仅内部网址端点暴露此设置值) - 只验证对于暂存它是否具有应该用于暂存的值。与单元测试相比,反馈略有延迟
  • 通过Django自己以外的测试框架测试它 - 即:在每个测试中编写unittest s(或py.test s)并在这些测试中bootstrap django(虽然你需要一种方法来导入和操纵设置)
  • 通过操作系统的环境(我们使用envdir a&#39; la 12 factor app)以及可以进行测试的管理命令,使用覆盖设置的组合( s) - 例如:MY_SETTING='overridden value' INSTALLED_APPS='dummy_app' EXPECTED_OUTCOME='whatever' python manage.py ensure_app_config_initialized_as_expected
  • 查看Django's own app init tests apps.clear_cache() with override_settings(INSTALLED_APPS=['test_app']): config = apps.get_app_config('test_app') assert config.... 可行,但我从来没有尝试过

答案 1 :(得分:1)

您似乎在Django中碰到了ready中的documented limitation(向下滚动到警告)。您可以在ticket that prompted the edit中查看讨论。该票证专门涉及数据库交互,但是相同的限制将应用于测试ready函数的所有工作-即在ready期间使用生产(而非测试)设置。

根据票证,“请勿使用ready”听起来像是官方的回答,但是除非发现他们将我引导到功能上等效的地方来运行这种初始化代码,否则我认为这种态度没有用处。 ready似乎是在启动时运行一次的最正式的地方。

我建议不要(重新)调用ready,而是让ready调用第二个方法。在测试案例中导入并使用第二种方法。您的测试不仅会更干净,而且可以将测试用例与任何其他ready逻辑(如附加信号)隔离开来。还有一个context manager可用于简化测试:

@override_settings(SOME_SETTING='some-data')
def test(self):
    ...

def test(self):
    with override_settings(SOME_SETTING='some-data'):
        ...

P.S。我们通过检查系统的迁移状态来解决ready中的几个可能的问题:

def ready(self):
    # imports have to be delayed for ready
    from django.db.migrations.executor import MigrationExecutor
    from django.conf import settings
    from django.db import connections, DEFAULT_DB_ALIAS

    executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS])
    plan = executor.migration_plan(executor.loader.graph.leaf_nodes())
    if plan:
        # not healthy (possibly setup for a migration)
        return

    ...

也许可以做类似的事情以防止在测试期间执行。系统以某种方式知道(最终)切换到测试设置。我认为您可以在相同条件下跳过执行。