SpecRec for Python
Make legacy Python code testable with minimal changes
Installation
pip install specrec-python
ObjectFactory: Breaking Hard Dependencies
Turn untestable legacy code into testable code by replacing direct instantiation with factory calls.
The Problem
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 user
The Solution
Replace new with create():
from specrec 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 user
Writing Tests
Basic Test Setup
from specrec 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"
Persistent Test Doubles
from specrec 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 too
API Reference
Core Functions
create(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
Advanced Usage
Custom Factory Instance
from specrec import ObjectFactory
# Create dedicated factory
api_factory = ObjectFactory()
create_api_service = api_factory.create(ApiService)
service = create_api_service("https://api.example.com")
Constructor Parameter Tracking
from specrec.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"
Object Registration
from specrec import register_object, get_registered_object
config = DatabaseConfig()
object_id = register_object(config, "db-config")
# Later retrieve it
retrieved = get_registered_object("db-config")
Requirements
- Python 3.8+
- typing-extensions (for Python < 3.10)
License
PolyForm Noncommercial License 1.0.0
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
specrec_python-0.1.1.tar.gz
(16.0 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file specrec_python-0.1.1.tar.gz.
File metadata
- Download URL: specrec_python-0.1.1.tar.gz
- Upload date:
- Size: 16.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.11.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8f59c21d00928784fdfbd4bd9b5d91eecb25f9c47c2ba969e69c416a9b8548f3
|
|
| MD5 |
ec92d10d87c2e04a1cd5adddd0572903
|
|
| BLAKE2b-256 |
f0fd3bd974c62cccf157535c2fba0fb66fe4ce39aaddf6fdcc10da31698ae820
|
File details
Details for the file specrec_python-0.1.1-py3-none-any.whl.
File metadata
- Download URL: specrec_python-0.1.1-py3-none-any.whl
- Upload date:
- Size: 10.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.11.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
90a4ade23c83ca9eca4981a93cab084b72963bfac03bd24ee25bd02bd6dd7352
|
|
| MD5 |
4b4041e3d4412afa2b959fa9c8f297b7
|
|
| BLAKE2b-256 |
5e9b58a6c7e4defdbeaa5599db0e9c700bc64a6ceb4414b8a0e16aabdd0658b6
|