为什么在 pytest 中未覆盖环境变量?

Why are environment variables not being overridden in pytest?

提问人:741852963 提问时间:10/25/2023 更新时间:10/25/2023 访问量:54

问:

我正在为一个基本的 flask 应用程序编写一些单元测试。 目前我正在测试 config.py 文件(主要是为了学习,但也是为了测试依赖于环境的数据库配置)。

该文件如下所示:

config.py

from os import environ as env
from dotenv import load_dotenv

load_dotenv()


class Config:
    ENVIRONMENT = env['CONFIG_MODE']
    SQLALCHEMY_TRACK_MODIFICATIONS = True
    SECRET_KEY = env['SECRET_KEY']

    # app deployed on Heroku - heroku sets DATABASE_URL to postgres://, SQLALCHEMY needs postgresql://
    if ENVIRONMENT == "development":
        SQLALCHEMY_DATABASE_URI = env['DEVELOPMENT_DATABASE_URL']
    else:
        SQLALCHEMY_DATABASE_URI = env.get('DATABASE_URL').replace("://", "ql://", 1)

这将从 .env 文件中提取环境变量

.env 域名

CONFIG_MODE = 'development'
DEVELOPMENT_DATABASE_URL = 'postgresql://usr:pwd@localhost:5432/db'

FLASK_APP=app
SECRET_KEY='XXX'

我设置了一个基本的 pytest 单元测试来检查SQLALCHEMY_DATABASE_URL是否设置正确,具体取决于CONFIG_MODE env 变量是“开发”还是“非开发”。

test_config.py

import os


class TestConfigDev:
    development_database_url = 'development_database_url'
    os.environ['DEVELOPMENT_DATABASE_URL'] = development_database_url
    os.environ['CONFIG_MODE'] = 'development'
    from service_authentication.api.config import Config
    config = Config

    def test_sqlalchemy_track_modifications(self):
        """
        GIVEN: SQLALCHEMY_TRACK_MODIFICATIONS variable exists
        WHEN: it is queried
        THEN: it is True
        """
        self.sqlalchemy_track_modifications = self.config.SQLALCHEMY_TRACK_MODIFICATIONS
        assert self.sqlalchemy_track_modifications

    def test_sqlalchemy_database_uri_dev(self):
        """
        GIVEN: CONFIG_MODE is development
        AND GIVEN: DEVELOPMENT_DATABASE_URL is set
        AND GIVEN: SQLALCHEMY_DATABASE_URI variable exists
        WHEN: it is queried
        THEN: it returns the DEVELOPMENT_DATABASE_URL
        """
        self.sqlalchemy_database_uri = self.config.SQLALCHEMY_DATABASE_URI
        assert self.sqlalchemy_database_uri == self.development_database_url


class TestConfigNotDev:
    database_url = 'postgres://database_url'
    os.environ['DATABASE_URL'] = database_url
    os.environ['CONFIG_MODE'] = 'not_development'
    from service_authentication.api.config import Config
    config = Config

    def test_sqlalchemy_database_uri_not_dev(self):
        """
        GIVEN: CONFIG_MODE is not development
        AND GIVEN: DATABASE_URL is set
        AND GIVEN: SQLALCHEMY_DATABASE_URI variable exists
        WHEN: it is queried
        THEN: it returns the modified DATABASE_URL
        """
        self.sqlalchemy_database_uri = self.config.SQLALCHEMY_DATABASE_URI
        self.database_url = self.database_url.replace("://", "ql://", 1)
        assert self.config.ENVIRONMENT == 'not_development'
        assert self.sqlalchemy_database_uri == self.database_url

TestConfigDev 测试通过,没有问题。

TestConfigNotDev 测试失败 - 尽管 ENVIRONMENT 变量在类定义中被重写为“not_development”,但仍设置为“development”。

我已将CONFIG_MODE作为 .env 中的变量集删除 - 将其实例化隔离到 test_config.py 文件中。问题仍然存在。

我尝试将它们分成两个测试文件(test_config_dev.py 和 test_config_not_dev.py)。以先运行者为准,设置 ENVIRONMENT 变量。

考虑到问题是 env 变量是在运行时设置的,我尝试在覆盖 env 变量之前和之后在 TestConfigNotDev 类中使用,但它仍然失败,因为 ENVIRONMENT 变量保持为“开发”。import osimportlib.reload(os)

奇怪的是,DEVELOPMENT_DATABASE_URL变量在 TestConfigDev 中被重写。

这意味着环境变量的设置方式正在发生其他事情,而我忽略了这一点。鉴于环境变量的基本情况,在我进一步讨论之前,我想先了解这里发生了什么。

python pytest .env

评论

1赞 MrBean Bremen 10/25/2023
您不是在测试期间更改环境变量,而是在加载时更改环境变量。您需要将它们设置在类范围的夹具中(可能使用 )。monkeypatch.setenv()
0赞 741852963 10/25/2023
谢谢 - 所以发生的事情是在 pytest 加载所有准备好进行测试的东西时,它设置了 env 变量:这些变量不能被测试中的代码更改。但是,如果我想这样做,那么我需要设置一个固定装置,上面写着“以不同的方式对待这个班级 - 这是我希望你做出的改变”?
2赞 MrBean Bremen 10/25/2023
这并不特定于 pytest。如果在类级别执行代码,则在模块加载期间将执行一次代码,因此两个类的方法中将具有相同的值。在您的例子中,您需要在类的第一次测试开始之前设置变量的代码,这就是类范围的夹具的用途。“使用”可确保在运行类中的所有测试后将该值重置为原始值。monkeypatch.setenv()
0赞 741852963 10/25/2023
谢谢你,非常有帮助

答:

1赞 741852963 10/25/2023 #1

来自 MrBean Bremen 的评论:

“您不是在测试期间更改环境变量,而是在加载时更改环境变量。你需要把它们设置在一个类范围的固定装置中(可能使用monkeypatch.setenv())。