Wraps timecop (github.com/travisjeffery/timecop), the Ruby time-mocking gem: Timecop.freeze, Timecop.travel, Timecop.scale (time-speedup), Timecop.return (cleanup), and RSpec-friendly helpers. Use when testing Ruby/Rails code that calls Time / Date / DateTime.
77
97%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
timecop is the canonical Ruby gem for time mocking. Per
github.com/travisjeffery/timecop,
it patches Time.now, Date.today, DateTime.now, and
Time.new to return controllable values.
# Gemfile
group :test do
gem 'timecop'
endbundle installrequire 'timecop'
Timecop.freeze(Time.local(2026, 5, 20, 14, 30)) do
expect(Time.now.strftime('%Y-%m-%d')).to eq('2026-05-20')
end
# Time auto-restored after blockTimecop.travel(Time.local(2026, 12, 31, 23, 59, 0)) do
sleep 5 # Real sleep
expect(Time.now).to be_within(6.seconds).of(Time.local(2026, 12, 31, 23, 59, 5))
endfreeze pauses; travel keeps the clock ticking from the offset.
Timecop.freeze(Time.local(2026, 5, 20, 14, 30))
# ... test code
Timecop.return # RestoreUse ensure blocks to guarantee cleanup:
def test_something
Timecop.freeze(...)
begin
# ...
ensure
Timecop.return
end
endTimecop.scale(3600) do # 1 real-second = 1 simulated-hour
start = Time.now
sleep 1 # 1 sec real = 1hr simulated
delta = Time.now - start
expect(delta).to be_within(60).of(3600)
endrequire 'active_support/time'
Time.zone = 'America/New_York'
Timecop.freeze(Time.zone.local(2026, 3, 8, 2, 30, 0)) do
# Behaviour depends on Time.zone library - Rails ActiveSupport
# has known DST-handling
expect(...).to ...
end# spec_helper.rb
RSpec.configure do |config|
config.after(:each) { Timecop.return } # Ensure cleanup
endRSpec.describe BookingController do
it 'rejects past dates' do
Timecop.freeze(Date.new(2026, 5, 20)) do
post :create, params: { date: '2026-05-19' }
expect(response.status).to eq(400)
end
end
endbundle exec rspec
bundle exec ruby -Itest test/jobs:
ruby-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: ruby/setup-ruby@v1
with: { bundler-cache: true }
- run: bundle exec rspec| Anti-pattern | Why it fails | Fix |
|---|---|---|
Forget Timecop.return | Cross-test contamination | RSpec after-each hook |
| Use freeze + sleep | sleep is real-time; clock stays frozen | Use travel or scale |
Hardcode Time.zone in tests | Cross-test config bleed | Save/restore tz per test |
| Test DST without ActiveSupport zone | Ruby Time doesn't track zones natively | Use ActiveSupport Time.zone |
Mock Time.now separately from Timecop | Conflict | Use Timecop exclusively |
Use Date.today without freeze | Tests fail at midnight | Always freeze |
| Treat travel and freeze identically | Different semantics | Use freeze when clock shouldn't advance |
Time.zone (ActiveSupport) and Time may diverge. Be
explicit which you're testing.Date.parse in Ruby uses real-system locale. Doesn't
honor Timecop.dst-transition-reference,
iso-8601-vs-rfc-3339-reference.libfaketime-c,
sinon-fake-timers-js,
jest-fake-timers,
freezegun-python,
mockclock-jvm.timezone-test-matrix-builder.