Practice · SOLID · DIP · Card 10
Why is the test flaky around midnight?
A method that reads Time.current directly. The CI test passes 99% of the time. The 1% is the bug.
The code
A class that decides whether a subscription is past its grace period.
class GracePeriodCheck
GRACE_DAYS = 7
def call(subscription)
return false if subscription.canceled_at.nil?
Time.current > subscription.canceled_at + GRACE_DAYS.days
end
end
# The test:
test "in grace period at the start" do
sub = subscriptions(:karim)
sub.update!(canceled_at: 3.days.ago)
assert_equal false, GracePeriodCheck.new.call(sub)
end
# Passes 99% of the time. Occasionally fails when CI runs near midnight. The question
Why is the test flaky, and what's the DIP-shaped fix that makes it stop?
Take a moment. The test references "3 days ago" and the production code references "now." Both move forward in time, but the test\'s "now" is the test\'s instantiation, and the production code\'s "now" is when Time.current evaluates. They might not be the same instant.
What's wrong
The test sets canceled_at = 3.days.ago at one wall-clock instant. The production code evaluates Time.current at a slightly later instant (microseconds, but real). The gap is normally invisible. Around the boundary where the test\'s "3 days ago" was just before midnight and the production code\'s "now" is just after, the date math flips and the assertion fails.
The deeper problem: the class hard-codes a dependency on the real clock. There\'s no way to test it deterministically without freezing the global clock (which works, but is a workaround, not a fix). Tools like freeze_time exist for exactly this reason; you needing them is a signal that your code is doing what DIP says not to.
Inject the clock
Treat "the current time" as a dependency. In production, the default is Time.current. In tests, you pass a frozen value.
class GracePeriodCheck
GRACE_DAYS = 7
def initialize(now: Time.current)
@now = now
end
def call(subscription)
return false if subscription.canceled_at.nil?
@now > subscription.canceled_at + GRACE_DAYS.days
end
end
# Test:
test "in grace period at day 3" do
now = Time.zone.parse("2026-01-15 12:00")
sub = subscriptions(:karim)
sub.update!(canceled_at: now - 3.days)
assert_equal false, GracePeriodCheck.new(now: now).call(sub)
end
# Deterministic. No `freeze_time`. No flakiness. The "current time"
# is something the test controls, not something the test inherits. This is "depend on abstractions, not concretions" expressed for time specifically. Same shape works for randomness (inject a random source), HTTP clients (inject a client), feature flags (inject a flag reader). When you can\'t hand a fake at the class boundary, the class is hard to test by definition.
Theory
For the test-seams angle in depth, read SOLID · DIP · Test Seams and Config Injection.