如何在Django中使用不同的设置进行unit testing?

有没有简单的机制来重写unit testing的Django设置? 我有一个经理在我的模型之一返回特定数量的最新对象。 它返回的对象数量由NUM_LATEST设置定义。

如果有人要改变设置,这有可能使我的testing失败。 如何覆盖setUp()上的设置,然后在tearDown()上恢复它们​​? 如果这是不可能的,有没有办法我可以猴子修补方法或模拟设置?

编辑:这是我的经理代码:

 class LatestManager(models.Manager): """ Returns a specific number of the most recent public Articles as defined by the NEWS_LATEST_MAX setting. """ def get_query_set(self): num_latest = getattr(settings, 'NEWS_NUM_LATEST', 10) return super(LatestManager, self).get_query_set().filter(is_public=True)[:num_latest] 

pipe理器使用settings.NEWS_LATEST_MAX .NEWS_LATEST_MAX切片查询集。 如果设置不存在, getattr()仅用于提供默认值。

编辑:这个答案适用于如果你想改变less数 特定testing的设置。

由于Django 1.4,在testing过程中可以覆盖设置: https : //docs.djangoproject.com/en/dev/topics/testing/tools/#overriding-settings

TestCase将有一个self.settings上下文pipe理器,还会有一个@override_settings修饰器,可以应用于testing方法或整个TestCase子类。

这些function在Django 1.3中尚不存在。

如果要更改所有testing的设置,则需要创build一个单独的testing设置文件,以便从主设置文件加载和覆盖设置。 在其他答案中有几个很好的方法来解决这个问题。 我已经看到了hspander和dmitrii的方法的成功变化。

您可以对UnitTest子类进行任何您喜欢的操作,包括设置和读取实例属性:

 from django.conf import settings class MyTest(unittest.TestCase): def setUp(self): self.old_setting = settings.NUM_LATEST settings.NUM_LATEST = 5 # value tested against in the TestCase def tearDown(self): settings.NUM_LATEST = self.old_setting 

由于djangotesting用例运行单线程,但是我很好奇还有什么可以修改NUM_LATEST值? 如果这个“别的东西”是由你的testing例程触发的,那么我不确定任何数量的猴子补丁都能保存testing,而不会影响testing本身的准确性。

更新 :下面的解决scheme只需要Django 1.3.x和更早的版本。 对于> 1.4请参阅slinkp的答案 。

如果您经常在testing中更改设置并使用Python≥2.5,这也很方便:

 from contextlib import contextmanager class SettingDoesNotExist: pass @contextmanager def patch_settings(**kwargs): from django.conf import settings old_settings = [] for key, new_value in kwargs.items(): old_value = getattr(settings, key, SettingDoesNotExist) old_settings.append((key, old_value)) setattr(settings, key, new_value) yield for key, old_value in old_settings: if old_value is SettingDoesNotExist: delattr(settings, key) else: setattr(settings, key, old_value) 

那你可以这样做:

 with patch_settings(MY_SETTING='my value', OTHER_SETTING='other value'): do_my_tests() 

尽pipe在运行时覆盖设置configuration可能会有所帮助,但我认为您应该创build一个单独的文件进行testing。 这节省了大量的testingconfiguration,这将确保您永远不会做不可逆转的事情(如清理临时数据库)。

假设你的testing文件存在于'my_project / test_settings.py'中,添加

 settings = 'my_project.test_settings' if 'test' in sys.argv else 'my_project.settings' 

在你的manage.py中。 这将确保当您运行python manage.py test ,只使用test_settings。 如果您正在使用pytest等其他testing客户端,可以轻松地将其添加到pytest.ini中

运行testing时可以传递--settings选项

 python manage.py test --settings=mysite.settings_local 

尝试修复一些doctests时发现这一点…为了完整我想提到,如果你要修改设置时,使用doctests,你应该做的之前导入其他任何…

 >>> from django.conf import settings >>> settings.SOME_SETTING = 20 >>> # Your other imports >>> from django.core.paginator import Paginator >>> # etc 

如果您的生产环境和testing环境configuration之间没有太多差异,那么@override_settings是非常棒的。

在其他情况下,你最好只是有不同的设置文件。 在这种情况下,您的项目将如下所示:

 your_project your_app ... settings __init__.py base.py dev.py test.py production.py manage.py 

所以你需要在base.py设置最多的设置,然后在其他文件中,你需要从那里导入所有的东西,并覆盖一些选项。 以下是你的test.py文件的样子:

 from .base import * DEBUG = False DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'app_db_test' } } PASSWORD_HASHERS = ( 'django.contrib.auth.hashers.MD5PasswordHasher', ) LOGGING = {} 

然后你需要在@MicroPyramid的答案中指定--settings选项,或者指定DJANGO_SETTINGS_MODULE环境variables,然后你可以运行你的testing:

 export DJANGO_SETTINGS_MODULE=settings.test python manage.py test