【问题标题】:django - how to detect test environment (check / determine if tests are being run)django - 如何检测测试环境(检查/确定是否正在运行测试)
【发布时间】:2011-05-04 13:08:18
【问题描述】:

如何检测是否正在测试环境中调用视图(例如,来自manage.py test)?

#pseudo_code
def my_view(request):
    if not request.is_secure() and not TEST_ENVIRONMENT:
        return HttpResponseForbidden()

【问题讨论】:

  • request 有什么属性?里面有提示吗?
  • 当您说“测试环境”时,您的意思是“运行测试时”吗?如果是这样,你到底为什么要这样做?仅在您运行测试时才有效的特殊情况代码意味着您实际上根本没有测试您的真实代码,那么有什么意义呢?
  • 值得注意的是,您可以为安全视图创建@https_only 包装器,而不是在视图中使用手动逻辑。在https_only 中,您可以在需要时使用https 发送重定向,或者在那里提出您的异常。

标签: django unit-testing


【解决方案1】:

如果您是针对不同环境的多个设置文件,您只需创建一个设置文件进行测试。

例如,您的设置文件是:

your_project/
      |_ settings/
           |_ __init__.py
           |_ base.py  <-- your original settings
           |_ testing.py  <-- for testing only

在您的 testing.py 中,添加 TESTING 标志:

from .base import *

TESTING = True

在您的应用程序中,您可以访问settings.TESTING 以检查您是否处于测试环境中。

要运行测试,请使用:

python manage.py test --settings your_project.settings.testing

【讨论】:

    【解决方案2】:

    虽然没有官方方法可以查看我们是否处于测试环境中,但 django 实际上为我们留下了一些线索。 默认情况下 Django 的测试运行器automatically redirects all Django-sent email to a dummy outbox。这是通过在名为setup_test_environment 的函数中替换EMAIL_BACKEND 来实现的,该函数又由DiscoverRunner 的method 调用。因此,我们可以检查 settings.EMAIL_BACKEND 是否设置为 'django.core.mail.backends.locmem.EmailBackend'。这意味着我们处于测试环境中。

    一个不那么老套的解决方案是跟随开发人员的领导,通过子类化 DisoverRunner 添加我们自己的设置,然后覆盖 setup_test_environment 方法。

    【讨论】:

      【解决方案3】:

      还有一种方法可以在 Django 的单元测试中临时覆盖设置。对于某些情况,这可能是一种更简单/更清洁的解决方案。

      您可以在测试中执行此操作:

      with self.settings(MY_SETTING='my_value'):
          # test code
      

      或者将其作为装饰器添加到测试方法上:

      @override_settings(MY_SETTING='my_value')
      def test_my_test(self):
          # test code
      

      您还可以为整个测试用例类设置装饰器:

      @override_settings(MY_SETTING='my_value')
      class MyTestCase(TestCase):
          # test methods
      

      有关更多信息,请查看 Django 文档:https://docs.djangoproject.com/en/1.11/topics/testing/tools/#django.test.override_settings

      【讨论】:

      • 我在 django 2.2 上尝试了所有这 3 种方法,但似乎都没有工作(编辑:直到我意识到我正在导入 myapp.settings 而不是 django.conf.settings)。
      【解决方案4】:

      把它放在你的 settings.py 中:

      import sys
      
      TESTING = len(sys.argv) > 1 and sys.argv[1] == 'test'
      

      这将测试第二个命令行参数(在./manage.py 之后)是否为test。然后您可以从其他模块访问此变量,如下所示:

      from django.conf import settings
      
      if settings.TESTING:
          ...
      

      这样做有充分的理由:假设您正在访问一些后端服务,而不是 Django 的模型和数据库连接。那么您可能需要知道何时调用生产服务和测试服务。

      【讨论】:

      • 如果有一些管理命令需要在sys.argv中放入参数test怎么办?
      • @EmanuelePaolini 在这种情况下,您可以更具体并要求它是第二个参数(在 manage.py 之后):TESTING = len(sys.argv) &gt; 1 and sys.argv[1] == 'test'
      • 是的,这就是我正在做的事情,我建议一般这样做。
      • 这是一个脆弱的hack,如果有人使用nose 或py.test 来运行测试呢?
      • @Tobia 他们是备用测试运行程序,广泛使用参见nose.readthedocs.org/en/latestpytest.org/latest 我不想修补,我认为查看 argv 的方法不够健壮和通用。其他解决方案最适合恕我直言(如 travis-jensen rednaw 或 pymarco)。
      【解决方案5】:

      我认为最好的方法是使用他们自己的设置文件(即 settings/tests.py)运行您的测试。该文件可能如下所示(第一行从 local.py 设置文件导入设置):

      from local import *
      TEST_MODE = True
      

      然后进行ducktyping检查您是否处于测试模式。

      try:
          if settings.TEST_MODE:
              print 'foo'
      except AttributeError:
          pass
      

      【讨论】:

        【解决方案6】:

        创建您自己的 TestSuiteRunner 子类并更改设置或为您的应用程序的其余部分执行您需要的任何其他操作。您在设置中指定测试运行器:

        TEST_RUNNER = 'your.project.MyTestSuiteRunner'
        

        一般来说,您不想这样做,但如果您绝对需要它,它会起作用。

        from django.conf import settings
        from django.test.simple import DjangoTestSuiteRunner
        
        class MyTestSuiteRunner(DjangoTestSuiteRunner):
            def __init__(self, *args, **kwargs):
                settings.IM_IN_TEST_MODE = True
                super(MyTestSuiteRunner, self).__init__(*args, **kwargs)
        

        【讨论】:

        • 似乎是这里唯一不依赖于知道一些变量值的解决方案,比如 sys.argv 或测试服务器的名称......
        • 虽然对问题有不同的解决方案很好,但在运行时更改 django 设置似乎不是一个明智的选择
        • 这是最稳定、最通用、最灵活的解决方案。但是 django.test.simple 已被弃用。现在应该使用 django.test.runner.DiscoverRunner 作为测试运行器的父类
        • 另外,最好在setup_test_environment 方法中更改设置而不是__init__
        • 在 Django 1.8+ 中,使用from django.test.runner import DiscoverRunner as DjangoTestSuiteRunner
        【解决方案7】:

        借鉴@Tobia 的回答,我认为最好在 settings.py 中像这样实现:

        import sys
        try:
            TESTING = 'test' == sys.argv[1]
        except IndexError:
            TESTING = False
        

        这将防止它捕获诸如./manage.py loaddata test.json./manage.py i_am_not_running_a_test 之类的东西

        【讨论】:

        • 这不是很灵活,因为test 参数可以在另一个索引上。例如。以python manage.py test 运行,它位于索引 2 处。
        • @gertvdijk 然后将其更改为 'test' == sys.argv[2] :)
        • @glarrain 当然,但我的意思是,您事先并不知道它,因为这取决于用户如何调用测试运行器。
        • “这将阻止它捕获像./manage.py loaddata test.json这样的东西”。自从'test' in ['./manage.py', 'loaddata', 'test.json'] == False 以来,该命令无论如何都不会导致'test' in sys.argv 为真。
        • 托比亚的回答中的所有这些都是False
        【解决方案8】:

        看看request.META['SERVER_NAME']

        def my_view(request):
            if request.META['SERVER_NAME'] == "testserver":
                print "This is test environment!"
        

        【讨论】:

        • 我不会依赖攻击者可以访问的东西,我认为这可以通过标题以某种方式进行配置,并且可以被操纵以使人们相信它实际上来自测试套件外部的东西
        猜你喜欢
        • 2018-12-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-28
        • 2010-12-26
        • 2015-05-24
        • 1970-01-01
        相关资源
        最近更新 更多