【问题标题】:pytest modules using os.environ - How do I test it correctly?使用 os.environ 的 pytest 模块 - 如何正确测试它?
【发布时间】:2015-06-18 12:36:59
【问题描述】:

目前我正在编写一些 Webapp,但这次我想学习如何为它编写适当的测试(使用 pytest):)

我经常看到的一个非常常见的模式是使用环境变量来更改默认配置。 目前我正在努力如何正确测试它。

我已经准备了一些演示:

./app
./app/conf.py
./conftest.py
./run.py
./tests
./tests/test_demo.py

我的./app/conf.py 看起来像这样:

from os import environ

DEMO = environ.get('DEMO', 'demo')
TEST = environ.get('TEST', 'test')

启动./run.py 表明设置确实可以更改:

from os import environ

environ['DEMO'] = 'not a demo'
environ['TEST'] = 'untested'

from app import conf

if __name__ == '__main__':

    print(conf.DEMO)
    print(conf.TEST)

它按预期打印出not a demountested。伟大的。 (注意我在导入conf之前设置了环境变量)。

现在开始测试:./conftest.py 目前是空的,它只是帮助 pytest 定位 app 文件夹中的模块。

./tests/test_demo.py 包含以下内容:

def test_conf_defaults():
    from app import conf

    assert conf.DEMO == 'demo'
    assert conf.TEST == 'test'


def test_conf_changed(monkeypatch):
    monkeypatch.setenv('DEMO', 'tested demo')
    monkeypatch.setenv('TEST', 'demo test')

    from app import conf

    assert conf.DEMO == 'tested demo'
    assert conf.TEST == 'demo test'

    monkeypatch.undo()

如果我现在运行 pytest,test_conf_changed 会失败并显示 'demo' == 'tested demo' -> monkeypatch 函数没有修补环境。

如果我交换两个测试函数(所以test_conf_changed 首先运行),test_conf_defaults 将失败并显示'tested demo' == 'demo'

我的解释是——conf 第一次被导入时,它的初始设置一直停留在那里..

我如何告诉 pytest 完全重新导入conf 每个测试函数, 设置环境变量?

我现在被困在那里两天了-慢慢地我怀疑测试是否值得麻烦-请证明我错了:)

【问题讨论】:

  • 我不确定你想要实现什么:如果你想运行测试,你运行py.test,它会获取所有test_*.py 文件并在其中运行测试。如果您想进行演示,您可以创建一个类似./demos/demo.py 的文件并将其作为演示运行。我认为它们中的任何一个(当然不是 pytest)都不应该依赖于环境变量。
  • 是的,当您导入 conf 时,并且 conf 模块中的变量设置在全局命名空间中,它们会一直存在:仅在整个运行期间(python 会话)进行一次导入。如果你想继续阅读或设置 env.变量,在 conf.py 中创建一个函数,例如 updateenv(),然后每次需要从其他函数中运行它以更新 env 变量。
  • 我想要实现的目标:config.py 包含默认设置,但我想让它们可以更改。例如存储临时文件等的路径。
  • 现在我想测试一下,如果分配工作正常 - 我写的演示只是用于 stackoverflow - 它与我的真实应用程序无关 - 它只是显示基本原理。
  • 当然pytest应该独立于环境变量。我希望 pytest 设置一些变量(本地),然后测试我的代码(在 conf.py 中)是否使用这些变量正确运行...

标签: python environment-variables pytest monkeypatching


【解决方案1】:

感谢您的提示,Evert(conf 模块中的变量设置在全局命名空间中,它们一直存在)- 我想我现在明白了。

为了测试我的代码,我必须在设置环境变量后显式地重新导入conf。将./tests/test_demo.py 中的代码改成这样就可以了:

from importlib import reload

from app import conf


def test_conf_changed(monkeypatch):
    monkeypatch.setenv('DEMO', 'tested demo')
    monkeypatch.setenv('TEST', 'demo test')

    reload(conf)

    assert conf.DEMO == 'tested demo'
    assert conf.TEST == 'demo test'


def test_conf_defaults():

    reload(conf)

    assert conf.DEMO == 'demo'
    assert conf.TEST == 'test'

谢谢。

【讨论】:

    【解决方案2】:

    我在使用 pytestimportlib 验证导入时遇到了相同问题的变体。导入的模块可以选择使用环境变量覆盖某些设置。使用 monkeypatch 修补环境变量后,有必要针对导入的模块对象调用 importlib.reload 以便对修补后的变量进行评估,因为它们最初是在之前评估过的猴子补丁

    使用 importlib 示例添加 spky 答案的这种变体,以扩大答案范围以涵盖那些使用 importlib 的情况。

    import importlib
    
    def test_env_override(monkeypatch):
        monkeypatch.setenv('APP_ROOT', '/tmp')
        _temp = importlib.import_module('appconfig', 'APP_ROOT')
        importlib.reload(_temp)
        assert getattr(_temp, 'APP_ROOT') == '/tmp'
    

    【讨论】:

    • 虽然您的回答可能会解决问题,但 including an explanation 关于如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人。您可以编辑您的答案以添加解释并指出适用的限制和假设。 - From Review
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多