Lightweight dependency injection for legacy code. Replace object instantiation with testable factories.
Watch: Making Legacy Code Testable with ObjectFactory - Learn the problem ObjectFactory solves and see a simple example in action.
pip install global-object-factoryTurn untestable legacy code into testable code by replacing direct instantiation with factory calls.
Legacy code with hard dependencies:
class UserService:
def process_user(self, user_id):
# Hard to test - always hits real database
repo = SqlRepository("server=prod;database=users")
user = repo.get_user(user_id)
return userReplace new with create():
from global_object_factory import create
class UserService:
def process_user(self, user_id):
# Now testable - can inject test doubles
repo = create(SqlRepository)("server=prod;database=users")
user = repo.get_user(user_id)
return userfrom global_object_factory import create, set_one, context
def test_user_service():
# Create test double
mock_repo = MockSqlRepository()
mock_repo.users = {"123": User("John", "john@example.com")}
with context():
# Next create() call returns our mock
set_one(SqlRepository, mock_repo)
# Test the code
service = UserService()
user = service.process_user("123")
assert user.name == "John"from global_object_factory import set_always, clear_one
def test_multiple_calls():
mock_repo = MockSqlRepository()
with context():
# All create() calls return our mock
set_always(SqlRepository, mock_repo)
service = UserService()
user1 = service.process_user("123") # Uses mock
user2 = service.process_user("456") # Uses mock toocreate(cls)- Returns a factory function for the classcreate_direct(cls, *args, **kwargs)- Create instance directlyset_one(cls, instance)- Return test double once, then normal instancesset_always(cls, instance)- Always return test doubleclear_one(cls)- Clear test doubles for specific typeclear_all()- Clear all test doublescontext()- Context manager for automatic cleanup
from global_object_factory import ObjectFactory
# Create dedicated factory
api_factory = ObjectFactory()
create_api_service = api_factory.create(ApiService)
service = create_api_service("https://api.example.com")from global_object_factory.interfaces import IConstructorCalledWith, ConstructorParameterInfo
from typing import List
class TrackedService(IConstructorCalledWith):
def __init__(self, host: str, port: int):
self.host = host
self.port = port
self.constructor_params: List[ConstructorParameterInfo] = []
def constructor_called_with(self, params: List[ConstructorParameterInfo]) -> None:
self.constructor_params = params
# Usage
service = create(TrackedService)("localhost", 8080)
print(service.constructor_params[0].name) # "host"
print(service.constructor_params[0].value) # "localhost"from global_object_factory import register_object, get_registered_object
config = DatabaseConfig()
object_id = register_object(config, "db-config")
# Later retrieve it
retrieved = get_registered_object("db-config")- Python 3.8+
- typing-extensions (for Python < 3.10)
PolyForm Noncommercial License 1.0.0
