software-design/dependency-injection-why-a-test-passes-at-9am-and-fails-at-3pm.md
Dependency injection: why a test passes at 9am and fails at 3pm
A test that passes in the morning and fails in the afternoon, with no change to the code, is a puzzle many beginners meet. The cause is often a function that fetches its own clock, or its own email service, instead of being given one.
Run the same two tests many times in the page and watch one flicker between green and red while the other never changes. Then see the small fix, called dependency injection, in short Python examples you can run yourself.

You write a function, and you write a test for it. The test passes. You run it again in the afternoon and it fails, even though you changed nothing. Where did the failure come from?
Often the function is reaching out and grabbing something by itself: the current time, a random number, a database, the internet. This article is about the simple fix that fixes it. Programmers call it dependency injection, which is a grand name for a small idea: hand the function what it needs, instead of letting it fetch things on its own.
A function that fetches its own clock
Here is a small Python function. A dependency is anything a piece of code needs in order to do its job. This function has one: the clock.
from datetime import datetime
def greeting():
hour = datetime.now().hour
if hour < 12:
return "Good morning"
return "Good afternoon"
print(greeting()) # "Good morning" before noon, "Good afternoon" after
The function decides for itself where the time comes from: it calls datetime.now(), which asks the computer for the real time. Now imagine the test:
assert greeting() == "Good morning"
Run before noon, this passes. Run after noon, it fails. The test is checking the function and the time you happened to run it. A test whose result changes when the code did not change is called flaky, and flaky tests are expensive: people stop trusting them.
Hand it in instead
The fix is to move the decision out of the function. Instead of fetching the hour, the function receives it as a parameter:
from datetime import datetime
def greeting(hour):
if hour < 12:
return "Good morning"
return "Good afternoon"
# The test decides the hour, so the answer never changes:
assert greeting(9) == "Good morning"
assert greeting(15) == "Good afternoon"
print("tests passed")
# Real code passes in the real hour:
print(greeting(datetime.now().hour))
Running this prints tests passed, then a greeting that depends on when you run it. The function is the same logic as before. What changed is who is in charge of the clock: now the caller is. A test can say "pretend it is 9 o'clock" and get the same answer at 3 a.m. and at 3 p.m. Handing a dependency in from outside like this is dependency injection.
Try it: two tests, many times of day
Below, each button press runs both tests at a random hour of the day, as if you ran your test suite at different moments. The left test asks for the real clock. The right test chooses its own hour. (This page simulates the idea with a few lines of JavaScript that mirror the Python above; it does not run Python.)
After a few presses the left side shows a mix of green and red from the same code. The right side stays green every time, because nothing in it depends on when it runs.
It is not only about clocks
The same trouble appears whenever a function quietly reaches for something outside itself: sending an email, charging a card, reading a file, asking a web service. In a test you do not want a real email to go out to a real person. So you hand in a fake, a stand-in that looks like the real thing but just records what happened:
class FakeMailer:
def __init__(self):
self.sent = []
def send(self, to, text):
self.sent.append((to, text))
def welcome(user, mailer):
mailer.send(user, "Welcome, " + user + "!")
fake = FakeMailer()
welcome("[email protected]", fake)
print(fake.sent)
# [('[email protected]', 'Welcome, [email protected]!')]
The function welcome does not know, or care, whether mailer is a real mail service or a fake. It only needs something with a send method. In real use you would pass the real mailer; in a test you pass the fake and then look inside it to check that the right message was "sent".
What to take from this
You do not need a framework or special tool for this. In Python, JavaScript and most other languages it is just a parameter. A few habits that follow:
- If a test passes sometimes and fails sometimes, look for something the code fetches by itself: time, randomness, files, the network.
- Give the function that thing as a parameter, and let the caller decide what to pass.
- Keep the part that does the real fetching small and at the edge of your program, so the rest of the code is easy to test.
- Do not add parameters for everything. A function that only does arithmetic needs no injection at all.
Next time a test feels unreliable, ask one question: what is this code grabbing that I do not control? Then let it be handed over instead.