Python Module Reload for Class Static Fields
Contents
Challenge
I was doing a little refactor for a codebase I worked in. The idea is to collect all free form environment variables into one class to have a centralized place to manage them all to simplify maintanence work.
It was a simple task on the surface, but I ran into problems when structuring my unittest.
The challange was realted to how Python handles class static variables.
Python Class executes once when it is first loaded and then cached. Consecutive calls uses the cashed value to avoid duplicate work.
For example if we have the following code:
# project_root/config.py
Class MyDemoClass:
NAMESPACE = os.getenv("NAMESPACE)
DEV_ENV = os.getenv("DEV_ENV")
The MyDemoClass.NAMESPACE
and MyDemoClass.DEV_ENV
are class static fields that all instances of MyDemoClass shares.
Solution
To write proper unittest, I need to have the power to force reload static fields for a class.
Before my test run, I supplied mocked os.environ
. However because Python runtime handles when the MyDemoClass
is loaded, by the time I supplied the mock in my unittest, the class may have already be loaded and invoked all the os.getenv
too early.
So the workaround I found is to force reload the MyDemoClass
module after I have mocked out the os.environ
sys varaible.
Like this:
# project_root/tests/test_mydemoclass.py
import unittest
from unittest.mock import patch
import config
from importlib import reload
Class TestMyDemoClass(unittest.Testcase):
# https://stackoverflow.com/questions/437589/how-do-i-unload-reload-a-python-module
@pathc.dict('os.environ', {'NAMESPACE' : 'my_test_namespace', 'DEV_ENV' : 'my_test_dev_env'})
def test_load_env(self):
reload(config)
from config import MyDemoClass
self.assertEqual("my_test_namespace", MyDemoClass.NAMESPACE)
Note that reload
is the tool I used to force reload a module (so that the os.getenv
uses the mock value), and the from config import MyDemoClass
allows me to use the just freshly reloaded MyDemoClass
.
Author Michael
LastMod 2020-07-29