A Python implementation of the Money example from Kent Beck's "Test-Driven Development: By Example", originally designed by Ward Cunningham.
This project demonstrates Test-Driven Development (TDD) principles by implementing a multi-currency money system. The implementation follows the classic Money pattern originally designed by Ward Cunningham and featured in Kent Beck's TDD book, building functionality incrementally through failing tests. The result is a clean, Pythonic API that makes multi-currency financial calculations feel as natural as basic arithmetic.
- Multi-currency money representation with type safety
- Pythonic arithmetic operations using
+and*operators - Exchange-centric API for currency conversion
- Mathematical properties: Associative and distributive operations
- Polymorphic behavior - works seamlessly with Python's built-in functions
- Clean, minimal API with only
MoneyandCurrencyExchangeclasses - Expression-based calculations with automatic reduction (internal implementation)
- Comprehensive test coverage with 18 test cases
- Test-driven development approach throughout
MoneyPython/
βββ src/
β βββ money.py # Main Money class implementation
βββ tests/
β βββ test_money.py # Test cases
βββ README.md
βββ LICENSE
from money import Money, CurrencyExchange
# Create money objects
five_dollars = Money(5, "USD")
ten_euros = Money(10, "EUR")
# Use Pythonic operators
result = five_dollars + ten_euros # Creates an expression (internal implementation)
doubled = five_dollars * 2 # Money(10, "USD")
tripled = 3 * five_dollars # Money(15, "USD")
# Currency conversion
exchange = CurrencyExchange()
exchange.add_rate("EUR", "USD", 0.85) # 1 EUR = 0.85 USD
# Reduce expressions to target currency
usd_result = exchange.reduce(five_dollars + ten_euros, "USD")
complex_result = exchange.reduce((five_dollars + ten_euros) * 2, "USD")
# Works with Python's built-in functions
portfolio = [Money(1000, "USD"), Money(500, "EUR"), Money(200, "GBP")]
total = sum(portfolio, 0) # Creates multi-currency expression
total_usd = exchange.reduce(total, "USD") # Convert to single currencyThe library exposes only two classes:
Money: Represents money with amount and currencyCurrencyExchange: Handles currency conversion and expression reduction
All other implementation details (like expression objects) are internal and not part of the public API.
# Addition
same_currency = Money(5, "USD") + Money(3, "USD") # Money(8, "USD")
different_currency = Money(5, "USD") + Money(10, "EUR") # Expression
# Multiplication (both directions)
doubled = Money(5, "USD") * 2 # Money(10, "USD")
tripled = 3 * Money(5, "USD") # Money(15, "USD")
# Reverse addition for sum() compatibility
result = 0 + Money(5, "USD") # Money(5, "USD")The library maintains mathematical correctness:
- Associativity:
(a + b) + c = a + (b + c) - Distributivity:
a * (b + c) = (a * b) + (a * c)(for same currency) - Expression-level equivalence for mixed currencies
# Valid operations
money + 0 # Returns the same money object
money + other_money # Creates expression or returns sum
# Invalid operations (raise exceptions)
money + 5 # ValueError: Cannot add non-zero number
money + "hello" # TypeError: Cannot add str to MoneyWorks seamlessly with Python's built-in functions:
# Portfolio calculations
holdings = [Money(1000, "USD"), Money(500, "EUR"), Money(200, "GBP")]
total = sum(holdings, 0) # Just like summing numbers!
average = total * (1/len(holdings)) # Mathematical operations work naturally# Run all tests
python -m pytest tests/
# Run with verbose output
python -m pytest tests/ -v
# Run specific test categories
python -m pytest tests/ -k "associativity"
python -m pytest tests/ -k "polymorphic"python example.pypython portfolio_example.pyThis project follows TDD principles:
- Write a failing test
- Write the minimal code to make it pass
- Refactor while keeping tests green
- Repeat
- 18 comprehensive test cases covering all functionality
- Mathematical properties (associativity, distributivity)
- Edge cases (error handling, type validation)
- Real-world scenarios (portfolio calculations, currency risk)
MoneyPython/
βββ src/
β βββ __init__.py # Public API exports
β βββ money.py # Money and Sum classes
β βββ currency_exchange.py # CurrencyExchange class
βββ tests/
β βββ __init__.py
β βββ test_money.py # Comprehensive test suite
βββ example.py # Basic usage examples
βββ portfolio_example.py # Advanced portfolio examples
βββ pyproject.toml # Project configuration
βββ requirements.txt # Dependencies
βββ README.md # This file
- Python 3.6+
- pytest (for running tests)
# Clone the repository
git clone <repository-url>
cd MoneyPython
# Install dependencies
pip install -r requirements.txt
# Run tests to verify installation
python -m pytest tests/This project demonstrates the TDD process following Ward Cunningham's original Money pattern:
- Started simple: Basic
Moneyclass with multiplication - Added complexity: Currency comparison and equality
- Introduced expressions: Multi-currency addition with
Sumclass - Made it Pythonic: Added
+and*operators - Enhanced usability: Added reverse operators for
sum()compatibility - Ensured correctness: Added mathematical property tests
- Refined API: Narrowed public interface to essential classes
Each feature was driven by a failing test, implemented minimally, then refactored for clarity and maintainability.
- Ward Cunningham: Original Money pattern design
- Kent Beck: TDD methodology and Money example in "Test-Driven Development: By Example"
- This implementation: Modern Python adaptation with enhanced features