'How to pass environment variables to pytest
Before I start executing the tests in my Python project, I read some environment variables and set some variables with these values read. My tests will run on the desired environment based on these values read.
For eg: Let's say the environment variables are called ENV_NAME
and ENV_NUMBER
Now, I would like to run the tests using py.test.
If I hard code these environment variables, for eg: ENV_NAME = 'staging', ENV_NUMBER = '5'
in my code and then run the tests by executing the py.test command at the root of the project directory, all the tests run successfully.
But, I don't want to hardcode these values. Is there a way, I can send these environment variables as command line arguments for py.test?
I was thinking more in the lines of
py.test -ENV_NAME='staging' -ENV_NUMBER='5'.
But, this is not working.
Solution 1:[1]
I finally found the answer i was looking for.
we can set the environment variables like this before running tests using py.test
ENV_NAME='staging' ENV_NUMBER='5' py.test
Solution 2:[2]
Another alternative is to use the pytest-env plugin. It can be configured like so:
[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
.
Note: you can explicitly run pytest with a custom config, if you only sometimes need to run a specialized environment set up:
pytest -c custom_pytest.ini
If you use PyCharm vs pytest-dotenv, this may be helpful
Solution 3:[3]
In addition to other answers. There is an option to overwrite pytest_generate_tests
in conftest.py
and set ENV variables there.
For example, add following into conftest.py
:
import os
def pytest_generate_tests(metafunc):
os.environ['TEST_NAME'] = 'My super test name| Python version {}'.format(python_version)
This code will allow you to grab TEST_NAME
ENV variable in your tests application. Also you could make a fixture:
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.
Solution 4:[4]
- I use monkey patch when I don't load environment variable variable outside function.
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')
- If you use PyCharm you can set environment variables,
[Run]
->[Edit Configuration]
->[Defaults]
->[py.tests]
->[Environment Variables]
Solution 5:[5]
There are few ways you can achieve this
If you dont want to use the environment variable , you can use pytest addoptions as https://docs.pytest.org/en/latest/example/simple.html
You can write a wrapper script like this to call enviornment variables
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)
in test_file.py you can use
env_n, env_n = pytest.config.getoption('pytest_args')
- Alternate method if you just want to pass the date not set enviornment variable
on command line you can use it as
py.test --testdata ="ENV_NAME:staging,ENV_NUMBER:5"
You can use in your test file
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"]
Solution 6:[6]
Following the idea provided by @tutuDajuju using pytest-env - an alternative would be to write a custom plugin leveraging pytest_load_initial_conftests. Might be useful especially when you don't want or can't install external dependencies.
Here's a quick example:
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 =========
Solution 7:[7]
Run export
inside a subshell (enclosing parenthesis) not to mess up local environment. Supply export with parameters from .env
file.
(export $(xargs < .env); pytest -svvvx api)
Solution 8:[8]
Similar to bad_coder had mentioned, you can do:
# test.py
def test_hello_world(monkeypatch):
# Setup
monkeypatch.setenv('HELLO', 'world')
# Test
result = hello_world()
# Verify
assert(result == 'world')
Solution 9:[9]
I needed to create a pytest.ini
file and pass the environment variables to the pytest
command. E.g:
In the pytest.ini file I set an empty value because it is overwritten by whatever you pass to the command line command:
[pytest]
MY_ENV_VAR=
Command line, with the actual value set:
$ 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.
Solution 10:[10]
Although the other answer works I think this one is more "hands-off" and automated and it simulates normal operation more. So I use python-dotenv
to load all variables form a file with 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
I think it is better if you want to test your whole logic of:
- Reading the variable from an
.env
file in a specific location and - Checking to see if the code you are testing is performing as expected based on the values in that file (maybe you could catch typos or other problems with your logic)
Solution 11:[11]
You could use monkeypatching, which is bundled with pytest
when your environment variables are dynamic.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow