在Django中覆盖单元测试设置无法正常工作

6
我尝试对我的Django程序进行单元测试,并想要检查settings.py文件中几个变量的多个值。
在文档中,"覆盖设置"部分描述了一种实现方式。但是,尽管我尝试了很多次,我仍然无法在测试运行时使更改生效。以下是我在最小示例上所做的摘要:
首先,创建一个新项目:
$ pyvenv virtualenv
$ cd virtualenv/
$ . bin/activate
(virtualenv) $ pip install django
(virtualenv) $ django-admin startproject myapp
(virtualenv) $ cd myapp/

然后,添加以下文件。

文件settings.py

...

TEST_VALUE = 'a'

文件 tests.py


from django.test import TestCase
from myapp.settings import TEST_VALUE

class CheckSettings(TestCase):

  def test_settings(self):
    self.assertEqual(TEST_VALUE, 'a')

  def test_modified_settings(self):
    with self.settings(TEST_VALUE='b'):
      self.assertEqual(TEST_VALUE, 'b')

当我运行测试时,我得到了以下结果:
$ ./manage.py test
Creating test database for alias 'default'...
F.
======================================================================
FAIL: test_modified_settings (myapp.tests.CheckSettings)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/virtualenv/myapp/tests.py", line 12, in test_modified_settings
    self.assertEqual(TEST_VALUE, 'b')
AssertionError: 'a' != 'b'
----------------------------------------------------------------------
Ran 2 tests in 0.001s

FAILED (failures=1)
Destroying test database for alias 'default'...

你可能已经注意到,TEST_VALUE 的原始值为 'a',我尝试通过 self.settings(TEST_VALUE='b') 来修改它,但是没有成功。

你可以在一个空项目中尝试它(Django 版本为 1.6.5)。

那么,我缺少什么才能使它正常工作?


1
只是猜测,但也许你必须通过 settings 访问设置 -- 例如 from testing_settings import settings,然后在你的断言中使用 settings.TEST_VALUE。改变你已经导入的值似乎有点太神奇了。 - Ismail Badawi
1
或者实际上是from django.conf import settings - Ismail Badawi
@IsmailBadawi:该死,你说得对!!!总结一下,我是通过from myapp.settings import TEST_VALUEsettings.py文件中获取变量的,但实际上我应该这样做:from django.conf import settings,然后使用settings.TEST_VALUE...感谢你的提示!它帮了我很多忙。 - perror
另外,请注意有一个 modify_settings() 装饰器。https://docs.djangoproject.com/en/1.9/topics/testing/tools/#django.test.modify_settings - Chemical Programmer
2个回答

10
简单总结一下Ismail Badawi的评论,实际上,我没有正确地从settings.py调用变量,因为我使用了from myapp.settings import TEST_VALUE,而应该使用from django.conf import settings,然后使用settings.TEST_VALUE。以下是正确的tests.py文件:
from django.test import TestCase
from django.conf import settings

class CheckSettings(TestCase):

  def test_settings(self):
    self.assertEqual(settings.TEST_VALUE, 'a')

  def test_modified_settings(self):
    with self.settings(TEST_VALUE='b'):
      self.assertEqual(settings.TEST_VALUE, 'b')

这里是./manage.py test的结果:

$ ./manage.py test
Creating test database for alias 'default'...
..
----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK
Destroying test database for alias 'default'...

所以,这个小故事的教训是,在使用时始终从django.conf而不是myapp.settings获取设置值。还要注意,如果您正在使用缓存,则可能会将settings.py的某些值存储在其中,并且可能会被其缓存的值掩盖。为此,Django文档中关于“覆盖设置”的说明提供了解决此类问题的提示:

在覆盖设置时,请确保处理应用程序代码使用缓存或类似功能的情况,即使更改了设置也会保留状态。 Django提供了django.test.signals.setting_changed信号,可以让您注册回调以在更改设置时清理和重置状态。

尽管如此,实现它的方式并没有给出。 这可能是下一个好问题...

1
另外,如果您在模块级别使用了一些设置(例如将它们存储在某些全局变量中以便更快地查找/更少的繁琐访问),则必须根据settings_changed信号重新加载它们。虽然在这里没有用处,但认为值得添加,因为它与此密切相关。 - spectras
@spectras:是的,你提到的很对,我会记下来的。 - perror

4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接