Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass environment variables to pytest

Tags:

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.

like image 620
sridhar249 Avatar asked Mar 21 '16 20:03

sridhar249


People also ask

Does pytest set environment variables?

Using pytest-env plugin We can use this plugin to set environment variables that don't really matter to the function implementations.


10 Answers

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

like image 144
tutuDajuju Avatar answered Oct 05 '22 04:10

tutuDajuju


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.

like image 30
skhalymon Avatar answered Oct 05 '22 04:10

skhalymon


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

like image 29
sridhar249 Avatar answered Oct 05 '22 03:10

sridhar249


  1. 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')
  1. If you use PyCharm you can set environment variables, [Run] -> [Edit Configuration] -> [Defaults] -> [py.tests] -> [Environment Variables]

enter image description here

like image 40
Miae Kim Avatar answered Oct 05 '22 02:10

Miae Kim


There are few ways you can achieve this

  1. If you dont want to use the environment variable , you can use pytest addoptions as https://docs.pytest.org/en/latest/example/simple.html

  2. 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')

  
  1. 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"]
like image 29
Macintosh_89 Avatar answered Oct 05 '22 02:10

Macintosh_89


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 =========
like image 42
pbajsarowicz Avatar answered Oct 05 '22 03:10

pbajsarowicz


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)

like image 32
simno Avatar answered Oct 05 '22 02:10

simno


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')
like image 30
Q. Qiao Avatar answered Oct 05 '22 04:10

Q. Qiao


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.

like image 27
clapas Avatar answered Oct 05 '22 04:10

clapas


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)
like image 30
KZiovas Avatar answered Oct 05 '22 02:10

KZiovas