我们正在尝试编写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初始化时测试失败行为改变。
答案 0 :(得分:2)
一些想法(需要不同的努力和自动保证):
unittest
s(或py.test
s)并在这些测试中bootstrap django(虽然你需要一种方法来导入和操纵设置)MY_SETTING='overridden value' INSTALLED_APPS='dummy_app' EXPECTED_OUTCOME='whatever' python manage.py ensure_app_config_initialized_as_expected
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
...
也许可以做类似的事情以防止在测试期间执行。系统以某种方式知道(最终)切换到测试设置。我认为您可以在相同条件下跳过执行。