How to Mock Environment Variables in Python
A context manager that injects and restores environment variables for isolated testing of config-dependent code.
Python code
34 linesimport os
class EnvInjector:
def __init__(self, mock_vars=None):
self.mock_vars = mock_vars or {}
self.original = {}
def __enter__(self):
for key, value in self.mock_vars.items():
if key in os.environ:
self.original[key] = os.environ[key]
os.environ[key] = value
return self
def __exit__(self, exc_type, exc_val, exc_tb):
for key in self.mock_vars:
if key in self.original:
os.environ[key] = self.original.pop(key)
else:
os.environ.pop(key, None)
def get_config_from_env():
return {
"host": os.environ.get("APP_HOST", "localhost"),
"port": int(os.environ.get("APP_PORT", "8080")),
"debug": os.environ.get("APP_DEBUG", "false").lower() == "true",
}
if __name__ == "__main__":
with EnvInjector({"APP_HOST": "example.com", "APP_PORT": "3000", "APP_DEBUG": "true"}):
print(get_config_from_env())
print(get_config_from_env())
Output
{'host': 'example.com', 'port': 3000, 'debug': True}
{'host': 'localhost', 'port': 8080, 'debug': False}
How it works
The EnvInjector class implements the context manager protocol with __enter__ and __exit__. Inside __enter__, it saves any existing environment variable values to self.original before overriding them, and __exit__ restores or removes those variables when the with block exits. The get_config_from_env function reads config values with defaults, so it transparently picks up the mocked values inside the block and the real values outside. This pattern isolates environment-dependent code without leaking state, making tests deterministic and side-effect free.
Common mistakes
- Forgetting to save original values, which breaks restoration after the block ends
- Using `os.environ[key]` instead of `.get(key)` for variables that may not exist
- Not handling multiple context managers nesting, which can corrupt saved state
Variations
- Use `unittest.mock.patch.dict(os.environ, {...})` for a one-liner alternative
- Apply `pytest`'s `monkeypatch.setenv` fixture for test-specific mocking
Real-world use cases
- Unit-testing service config loading that reads secrets like API keys or database URLs from env vars.
- Simulating different deployment environments (staging, production) in integration tests without changing the machine.
- Validating fallback logic when required environment variables are missing or misconfigured in CI.
Sponsored
More from Auth & security at scale
- ACME LetsEncrypt Mock Challenge Server in Python medium
- AES GCM encryption and decryption in Python medium
- Build a Mock OIDC Userinfo Endpoint in Python with Flask easy
- ChaCha20-Poly1305 mock in Python medium
- ECDH key agreement in Python with cryptography medium
- Enforce TLS 1.2 Minimum in Python easy
Keep learning
Related tutorials and quizzes for this topic.