Don't build your tools inside the house. Have them handed to you.
Dependency Injection (DI) is a design pattern where an object receives its dependencies from the outside, rather than creating them itself. This achieves Inversion of Control, making your code significantly more modular, reusable, and most importantly: testable.
Instead of hardcoding a dependency like a Database inside a class, you require it to be passed into the constructor (or initialized via a framework like Spring or Angular).
# VULNERABLE TO COUPLING (Hardcoded)
class UserService:
def __init__(self):
# Tied forever to Postgres! Hard to test without a real DB.
self.db = PostgresDB()
def get_user(self, id):
return self.db.query(id)
# SECURE (Dependency Injected)
class UserService:
def __init__(self, db: DatabaseInterface):
# It doesn't care what DB it is, as long as it fits the interface.
self.db = db
You write a `PaymentProcessor` that hardcodes `StripeAPI()`. A year later, the company switches to PayPal. You have to rewrite the `PaymentProcessor`. Worse, when writing unit tests, you accidentally charge a real credit card because the real Stripe API was hardcoded! If you had used DI, you could have easily passed a `MockPaymentAPI()` during tests.
What is the primary benefit of Dependency Injection when writing Unit Tests?