Skip to content

KentBeck/MoneyPython

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

MoneyPython

A Python implementation of the Money example from Kent Beck's "Test-Driven Development: By Example", originally designed by Ward Cunningham.

Overview

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.

Features

  • 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 Money and CurrencyExchange classes
  • Expression-based calculations with automatic reduction (internal implementation)
  • Comprehensive test coverage with 18 test cases
  • Test-driven development approach throughout

Project Structure

MoneyPython/
β”œβ”€β”€ src/
β”‚   └── money.py          # Main Money class implementation
β”œβ”€β”€ tests/
β”‚   └── test_money.py     # Test cases
β”œβ”€β”€ README.md
└── LICENSE

Usage

Basic Operations

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 currency

Public API

The library exposes only two classes:

  • Money: Represents money with amount and currency
  • CurrencyExchange: Handles currency conversion and expression reduction

All other implementation details (like expression objects) are internal and not part of the public API.

Key Features

1. Pythonic Operators

# 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")

2. Mathematical Properties

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

3. Error Handling

# 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 Money

4. Polymorphic Behavior

Works 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

Running Tests

# 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"

Examples

Basic Usage

python example.py

Portfolio Management

python portfolio_example.py

Development

This project follows TDD principles:

  1. Write a failing test
  2. Write the minimal code to make it pass
  3. Refactor while keeping tests green
  4. Repeat

Test Coverage

  • 18 comprehensive test cases covering all functionality
  • Mathematical properties (associativity, distributivity)
  • Edge cases (error handling, type validation)
  • Real-world scenarios (portfolio calculations, currency risk)

Project Structure

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

Requirements

  • Python 3.6+
  • pytest (for running tests)

Installation

# 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/

TDD Journey

This project demonstrates the TDD process following Ward Cunningham's original Money pattern:

  1. Started simple: Basic Money class with multiplication
  2. Added complexity: Currency comparison and equality
  3. Introduced expressions: Multi-currency addition with Sum class
  4. Made it Pythonic: Added + and * operators
  5. Enhanced usability: Added reverse operators for sum() compatibility
  6. Ensured correctness: Added mathematical property tests
  7. Refined API: Narrowed public interface to essential classes

Each feature was driven by a failing test, implemented minimally, then refactored for clarity and maintainability.

Credits

  • 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

About

Multi-currency arithmetic in Python

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages