TransWikia.com

Pytest environment fixtures not seen by imported class

Stack Overflow Asked by pmdaly on November 18, 2021

I’m trying to set up pytest so that whenever I run my tests (locally or in github actions), the environment variables all point to files and locations in my test directory instead of wherever they’re set to based on the user.

The problem is, the fixture changes are visible if I add an ipdb trace in the test_database function and print os.getenv('DB_URL') but the assert will always fail because the DataBase object always has the original non-mocked url (set in .bash_profile).

database.py

import h5py
import os

class DataBase:

    route = os.environ.get('DB_URL')

    def __init__(self):
        self.connected = False

    def connect(self):
        if not connected:
            self.db = h5py.File(self.route, 'r')
            self.connected = True

conftest.py

import os
import pytest

@pytest.fixture(autouse=True)
def mock_test_env(monkeypatch):
    cwd = os.getcwd()
    monkeypatch.setenv('DB_URL', cwd + '/sample_db.hdf5')

test_database.py

import pytest
from repo import DataBase

def test_database():
    db = DataBase()
    import ipdb; ipdb.set_trace()
    '''
    os.getenv('DB_URL') returns cwd + '/sample_db.hdf5'
    db.route returns original database, not the sample one above
    '''
    assert db.connected = False, 'DataBase must be instantiated to connected == False'

How do I globally set environment variables so all objects see the same envs?

One Answer

As others have mentioned in your comment that class variables to be avoided for this assignment because it's a constant which gets assigned the moment the import statement is scanned.

To better understand this situation, try placing the from repo import DataBase inside your method

def test_database():

Like this:

import os
import pytest

@pytest.fixture(autouse=True)
def mock_test_env(monkeypatch):
    cwd = os.getcwd()
    monkeypatch.setenv('DB_URL', cwd + '/sample_db.hdf5')

def test_database(mock_test_env):
    from repo import DataBase # <<< This here works
    db = DataBase()
    assert db.route == (os.getcwd() + '/sample_db.hdf5') # Works!

Now, when you place the from repo import Database at the start of the file, pytest would scan and start reading all the imports and starts initialising the blueprints and sets the value of router the moment its imported.

Read: When is the class variable initialised in Python?

So the ideal way would be to avoid maybe such important initialisation and assing the same in the constructor of the Database class. Thus ensuring it does calculate when needed.

I feel, I for one like to think of it like Explicit is better than implicit. from the Zen Of Python and do it like:

import h5py
import os

class DataBase:

    def __init__(self):
        self.route = os.environ.get('DB_URL')
        self.connected = False

    def connect(self):
        if not connected:
            self.db = h5py.File(self.route, 'r')
            self.connected = True

Answered by Nagaraj Tantri on November 18, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP