Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to monkeypatch the environment using pytest in conftest.py?

Tags:

python

pytest

I have a global object in my main file

# reporter.py

from os import environ
from influxdb import InfluxDBClient

influxdb_client = InfluxDBClient(host=environ['INFLUXCLOUD_HOST'],
                                 username=environ['INFLUXCLOUD_USERNAME'],
                                 password=environ['INFLUXCLOUD_PASSWORD'],
                                 ssl=True,
                                 timeout=4*60)

def foo():
    pass

I'm using pytest and I want to set faux values to these environment variables. I have the following in my conftest.py:

# conftest.py

import pytest

@pytest.fixture(scope='session', autouse=True)
def setup_env(monkeypatch):
    monkeypatch.setenv('INFLUXCLOUD_HOST', 'host')
    monkeypatch.setenv('INFLUXCLOUD_USERNAME', 'username')
    monkeypatch.setenv('INFLUXCLOUD_PASSWORD', 'password')

However, when I import reporter in my test file, I get a KeyError that INFLUXCLOUD_HOST is missing in the env.

Why does not pytest execute the setup_env and monkeypatch my environment? Is there a way to do so?

like image 909
Milan Cermak Avatar asked Oct 13 '17 15:10

Milan Cermak


1 Answers

The problem here lies in misunderstanding what the session-scoped fixture is.

To know which tests & autoused fixtures do exist, pytest needs to import the test files & conftest plugins. Then it scans the imported modules, and looks for the fixtures & test functions & test classes & etc. This is called "collection" in pytest terms.

Only after all tests are collected, pytest decides to execute them, and arranges the execution plan, and specifically when the fixtures are prepared. The session-scoped fixtures are prepared first and teared down last — before any tests start, and after all tests have finished.

However, importing of the test files & conftest assumes the execution of these modules — as importing of any other Python module, unrelated to pytest.

So, when you do import reporter from your test file, or even if you put that global variable directly to your test file, this module is executed, and it attempts to use the env vars. But the fixtures are not yet executed (and pytest does not know about their existence yet). Therefore, it fails.

Even if you will import reporter from inside of the test function, this will not help much, as pytest may try to import that reporter.py module before during the collection stage. Pytest would filter it out due to the absence of the test functions/classes, but the import attempt will be done and will fail.

The best solution here is to "pack" the client into a fixture and use that fixture instead of the global variable.

like image 149
Sergey Vasilyev Avatar answered Oct 04 '22 04:10

Sergey Vasilyev