travisjeffery / timecop

A gem providing "time travel", "time freezing", and "time acceleration" capabilities, making it simple to test time-dependent code. It provides a unified method to mock Time.now, Date.today, and DateTime.now in a single call.
MIT License
3.36k stars 223 forks source link

Timecop.freeze not working with expect().to receive #399

Closed rgaufman closed 1 year ago

rgaufman commented 1 year ago

I have this spec:

    context 'when another percentage_packet_loss' do
      before do
        Timecop.freeze(Time.local(1990))
      end

      after do
        Timecop.return
      end

      it 'updates device' do
        expect(Tether::Mqtt::Request).to receive(:new).with(
          '/api/v3/devices',
          device: {
            online: true,
            state_params_updated_at: Time.now.utc
          },
          token: 'my-token',
          method: :put,
          qos: 1
        ).once.and_call_original

        DevicePacketLossJob.perform
      end
    end

I have this callback that sets the time in the database in app/models/application_record.rb:

class ApplicationRecord < ActiveRecord::Base
  before_save :update_state_params_updated_at

  def update_state_params_updated_at
    self.state_params_updated_at = Time.now.utc
  end

When I run it the spec, I see:

       -    :state_params_updated_at=>1990-01-01 00:00:00.000000000 +0000}},
       +    :state_params_updated_at=>2023-02-12 13:44:18.011870000 +0000}},

So it seems the Time.now.utc inside of app/models/application_record.rb is returning the time now and is not the expected time from Timecop.

Any ideas?

I'm using latest versions of everything, just done bundle update:

timecop (0.9.6)
rspec (3.12.0)
rails (7.0.4.2)
ruby 3.1.3p185
rgaufman commented 1 year ago

Update, if I add this to the environments test.rb file, it does something different, but still doesn't work:

Rails.application.configure do
  config.after_initialize do
    # Set Time.now to Feb 12, 2023 10:05:00 AM (at this instant), but allow it to move forward
    t = Time.local(2023, 2, 12, 10, 5, 0)
    Timecop.travel(t)
  end

It does then seem to stub the date, but it still fails like this:

       -    :state_params_updated_at=>2023-02-12 10:05:00.377149999 +0000}},
       +    :state_params_updated_at=>2023-02-12 10:05:00.365396000 +0000}},

Any ideas?

joshuacronemeyer commented 1 year ago

Hey @rgaufman without seeing all your code and understanding the environment you're running in I can't troubleshoot this, but a couple things to consider: time cop is only stubbing out the time for the ruby process in your test. If DevicePacketLossJob is running through some queueing system in another process, timecop isn't doing anything there. Also timecop can't affect times that your DB system is generating, so things like current_timestamp running in SQL won't be affected. You probably know this stuff anyway, but just double check that delayed job or sidekiq is running DevicePacketLossJob inline in the spec process.

joshuacronemeyer commented 1 year ago

Going to close this since we haven't gotten actionable with this and no updates. Re-open if you need to.