How to pass environment variables to pytest

在我开始执行 Python 项目中的测试之前,我读取了一些环境变量,并用这些值读取了一些变量。我的测试将基于所读取的这些值在所需的环境上运行。

例如: 让我们假设环境变量被称为 ENV_NAMEENV_NUMBER

现在,我想使用 py.test 运行这些测试。

如果我在代码中硬编码这些环境变量(例如: ENV_NAME = 'staging', ENV_NUMBER = '5') ,然后通过在项目目录的根目录中执行 py.test 命令来运行测试,那么所有测试都会成功运行。

但是,我不想硬编码这些值。有没有办法,我可以把这些环境变量作为 py.test 的命令行参数发送出去?

我想的是

py.test -ENV_NAME='staging' -ENV_NUMBER='5'.

But, this is not working.

161757 次浏览

有几种方法可以实现这一点

  1. 如果你不想使用这个环境变量,你可以使用 pytest addoptions 作为 < a href = “ https://docs.pytest.org/en/update/example/simple.html”rel = “ nofollow norefrer”> https://docs.pytest.org/en/latest/example/simple.html

  2. 您可以编写这样的包装器脚本来调用环境变量

    import os
    import py
    env_name = os.environ["ENV_NAME"]
    env_no = os.environ["ENV_NUMBER"]
    pytest_args=(env_name,env_no)
    pytest.main('-s' ,pytest_args,test_file.py)
    

在 test _ file. py 中 你可以利用

   env_n, env_n = pytest.config.getoption('pytest_args')


  

  1. 如果只想传递 date not set 环境变量,请使用替代方法

在命令行上,您可以将它用作

   py.test --testdata ="ENV_NAME:staging,ENV_NUMBER:5"

您可以在测试文件中使用

pytest_params = pytest.config.getoption('testdata')
params = pytest_params.split(":")
param_dict = dict(params[i:i+2] for i in range(0,len(params),2))
env_name = param_dict["ENV_Name"]

I finally found the answer i was looking for.

在使用 py.test 运行测试之前,我们可以像这样设置环境变量

ENV_NAME='staging' ENV_NUMBER='5' py.test

另一种选择是使用 (咒语)插件。它可以这样配置:

[pytest]
env =
HOME=~/tmp
D:RUN_ENV=test

the D: prefix allows setting a default value, and not override existing variables passed to py.test.

注意 : 如果只是有时需要运行专门的环境设置,那么可以使用自定义配置显式地运行 pytest:

pytest -c custom_pytest.ini

如果你使用 PyCharm vs pytest-dotenv这个可能会有帮助

  1. 当我不加载外部函数的环境变量变量时,我使用 Monkey patch。
import os


# success.py
def hello_world():
return os.environ["HELLO"]


# fail.py
global_ref = os.environ["HELLO"] # KeyError occurs this line because getting environment variable before monkeypatching


def hello_world():
return global_ref


# test.py
def test_hello_world(monkeypatch):
# Setup
envs = {
'HELLO': 'world'
}
monkeypatch.setattr(os, 'environ', envs)


# Test
result = hello_world()


# Verify
assert(result == 'world')
  1. 如果您使用 PyCharm,您可以设置环境变量,[Run]-> [Edit Configuration]-> [Defaults]-> [py.tests]-> [Environment Variables]

enter image description here

除了其他的答案。有一个选项覆盖 conftest.py中的 pytest_generate_tests和设置 ENV 变量。

例如,在 conftest.py中添加以下内容:

import os


def pytest_generate_tests(metafunc):
os.environ['TEST_NAME'] = 'My super test name| Python version {}'.format(python_version)

这段代码允许你在你的测试应用程序中抓取 TEST_NAME ENV 变量:

import os
import pytest


@pytest.fixture
def the_name():
return os.environ.get('TEST_NAME')

Also, this ENV variable will be available in your application.

我需要创建一个 pytest.ini文件 还有,将环境变量传递给 pytest命令:

在 pytest.ini 文件中,我设置了一个空值,因为它被传递给命令行命令的内容所覆盖:

[pytest]
MY_ENV_VAR=

命令行,并设置实际值:

$ MY_ENV_VAR=something pytest -c pytest.ini -s tests/**

I don't know why does it work like this. I just found out that it works as a result of mere trial and error, because the other answers didn't help me.

Following the idea provided by @tutuDajuju using pytest-env - an alternative would be to write a custom plugin leveraging Pytest _ load _ first _ concontest. Might be useful especially when you don't want or can't install external dependencies.

这里有一个简单的例子:

Project structure

.
├── __init__.py
├── pytest.ini
├── script.py
└── tests
├── __init__.py
├── plugins
│   ├── __init__.py
│   └── env_vars.py
└── test_script.py

Script.py

import os


FOOBAR = os.environ.get("FOOBAR")




def foobar():
return FOOBAR

test_script.py

from script import foobar




def test_foobar():
assert foobar() == "foobar"

Pytest.ini

[pytest]
addopts = -p tests.plugins.env_vars

env_vars.py

import os


import pytest




@pytest.hookimpl(tryfirst=True)
def pytest_load_initial_conftests(args, early_config, parser):
os.environ["FOOBAR"] = "foobar"

Example run:

$ python -m pytest tests -v
========= test session starts =========
platform darwin -- Python 3.8.1, pytest-5.4.1, py-1.8.1, pluggy-0.13.1 --
rootdir: /Users/user/pytest_plugins, inifile: pytest.ini
collected 1 item


tests/test_script.py::test_foobar PASSED                                                                                               [100%]


========= 1 passed in 0.01s =========

在子 shell 中运行 export(包括括括号)以避免破坏本地环境。

(export $(xargs < .env); pytest -svvvx api)

与 bad _ coder 提到的类似,你可以这样做:

# test.py
def test_hello_world(monkeypatch):
# Setup
monkeypatch.setenv('HELLO', 'world')


# Test
result = hello_world()


# Verify
assert(result == 'world')

虽然另一个答案工程,我认为这一个是更多的“放手”和自动化,它模拟正常操作更多。因此,我使用 python-dotenv加载所有变量形成一个文件与 load_dotenv(my_filepath):

import os
import pytest
from dotenv import load_dotenv
from core import ConfigService




def test_config_service():
"""This test ensures that the config service can read the environment
variables which define the location of the config.json files"""


load_dotenv("env/common.env")
config_service = ConfigService()
config_service.load()
assert config_service.config_folder_path is not None
assert config_service.config_folder_name is not None

我认为,如果你想测试你的整个逻辑:

  • 从特定位置的 .env文件中读取变量,然后
  • 根据文件中的值检查您正在测试的代码是否按预期执行(也许您可以捕获输入错误或逻辑中的其他问题)

您可以使用 monkeypatching,当您的环境变量是动态的时候,它与 pytest绑定在一起。