django在单元测试中设置环境变量

lol*_*ter 25 python django environment-variables foreman

我希望能够在我的Django应用程序中设置环境变量,以便能够运行测试.例如,我的观点依赖于几个API密钥.

有一些方法可以在测试期间覆盖设置,但我不希望它们被定义,settings.py因为这是一个安全问题.

我已经尝试在我的设置函数中设置这些环境变量,但这不能为Django应用程序提供值.

class MyTests(TestCase):
    def setUp(self):
        os.environ['TEST'] = '123'  # doesn't propogate to app
Run Code Online (Sandbox Code Playgroud)

当我在本地测试时,我只是有一个.env我运行的文件

foreman start -e .env web
Run Code Online (Sandbox Code Playgroud)

提供os.environ价值.但是在Django中unittest.TestCase它没有办法(我知道)设置它.

我怎么能绕过这个?

Dev*_*evy 34

test.support.EnvironmentVarGuard是一个内部API,可能会随着版本的变化而发生更改(向后不兼容).实际上,整个test包装仅供内部使用.在测试包文档页面上明确指出它是用于核心库的内部测试而不是公共API.(见下面的链接)

你应该patch.dict()在python的标准库中使用unittest.mock.它可以用作上下文管理器,装饰器或类装饰器.请参阅以下从官方Python文档中复制的示例代码.

import os
from unittest.mock import patch
with patch.dict('os.environ', {'newkey': 'newvalue'}):
    print(os.environ['newkey'])  # should print out 'newvalue'
    assert 'newkey' in os.environ  # should be True
assert 'newkey' not in os.environ  # should be True
Run Code Online (Sandbox Code Playgroud)

更新:对于那些没有彻底阅读文档且可能错过了该文档的人,请阅读更多的test软件包说明

https://docs.python.org/2/library/test.html

https://docs.python.org/3/library/test.html

  • @seb在你提供的链接中,它固定在`class test.test_support.EnvironmentVarGuard`上,换句话说,它是`test`包的一部分,它是**Python**的回归测试包.然后滚动页面ALL THE WAY UP,阅读标题后面第二行的注释:_注意测试包仅供Python内部使用.它是为Python的核心开发人员的利益而记录的.不鼓励在Python的标准库之外使用此包,因为此处提到的代码可以在Python版本之间更改或删除,恕不另行通知. (2认同)

lol*_*ter 12

正如@schillingt在评论中指出的那样,EnvironmentVarGuard是正确的方法.

from test.test_support import EnvironmentVarGuard # Python(2.7 < 3)
from test.support import EnvironmentVarGuard # Python >=3
from django.test import TestCase

class MyTestCase(TestCase):
    def setUp(self):
        self.env = EnvironmentVarGuard()
        self.env.set('VAR', 'value')

    def test_something(self):
        with self.env:
            # ... perform tests here ... #
            pass
Run Code Online (Sandbox Code Playgroud)

这会在上下文对象with语句的持续时间内正确设置环境变量.

  • python 3将它从test.support导入EnvironmentVarGuard`移动到` 但是,如果您不想依赖仅供内部使用的代码,则可以复制[EnvironmentVarGuard的python 2.7实现](https://github.com/python/cpython/blob/2.7/Lib/test/test_support .py#L957-L1001)进入你自己的代码 - 它非常简单. (3认同)
  • 引发导入错误.此外,EnvironmentVarGuard的文档声明:"警告测试包仅供Python内部使用.它是为Python的核心开发人员的利益而记录的.不鼓励在Python的标准库之外使用此包.这里可以更改或删除Python版本之间没有通知." (2认同)

Luc*_*s03 8

如果您在 Django 的settings.py文件中加载环境变量,如下所示:

import os
ENV_NAME = os.environ.get('ENV_NAME', 'default')
Run Code Online (Sandbox Code Playgroud)

你可以用这个:

from django.test import TestCase, override_settings

@override_settings(ENV_NAME="super_setting")
def test_...(self):
Run Code Online (Sandbox Code Playgroud)

  • 这是一个非常好的答案,因为通常的做法是将 `os.environ` 变量拉入 `settings.py`,然后在整个代码中使用 _those_ 变量(a-la `from django.conf import settings`)。例如,如果您的代码使用“my_obj = MyClass(arg=settings.MY_ENV_VAR)”,那么您需要使用“@override_settings()”包装器在单元测试中正确设置“MY_ENV_VAR”。如果您以任何其他方式设置 `os.environ` 变量,`my_obj` 将无法正确实例化 (2认同)

gbo*_*zee 5

使用EnvironmentVarGuard不是一个好的解决方案,因为它在某些环境中会失败,而在其他环境中会起作用。请参见下面的示例。

gitlab ci上的Python3.6环境

erewok提出了一个更好的解决方案,即需要使用unittest.mockpython3中的。

假设使用unittest

from unittest.mock import patch
class TestCase(unittest.TestCase):

    def setUp(self):
        self.env = patch.dict('os.environ', {'hello':'world'})

    def test_scenario_1(self):
        with self.env:
            self.assertEqual(os.environ.get('hello'), 'world')
Run Code Online (Sandbox Code Playgroud)

```