Coder Social home page Coder Social logo

travisjeffery / timecop Goto Github PK

View Code? Open in Web Editor NEW
3.3K 32.0 223.0 389 KB

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.

License: MIT License

Ruby 99.29% Dockerfile 0.25% Makefile 0.46%
rails ruby time test

timecop's Introduction

timecop

Gem Version Build Status

DESCRIPTION

A gem providing "time travel" and "time freezing" capabilities, making it dead simple to test time-dependent code. It provides a unified method to mock Time.now, Date.today, and DateTime.now in a single call.

INSTALL

bundle add timecop

FEATURES

  • Freeze time to a specific point.
  • Travel back to a specific point in time, but allow time to continue moving forward from there.
  • Scale time by a given scaling factor that will cause time to move at an accelerated pace.
  • No dependencies, can be used with any ruby project
  • Timecop api allows arguments to be passed into #freeze and #travel as one of the following:
    • Time instance
    • DateTime instance
    • Date instance
    • individual arguments (year, month, day, hour, minute, second)
    • a single integer argument that is interpreted as an offset in seconds from Time.now
  • Nested calls to Timecop#travel and Timecop#freeze are supported -- each block will maintain its interpretation of now.
  • Works with regular Ruby projects, and Ruby on Rails projects

USAGE

Run a time-sensitive test

joe = User.find(1)
joe.purchase_home()
assert !joe.mortgage_due?
# move ahead a month and assert that the mortgage is due
Timecop.freeze(Date.today + 30) do
  assert joe.mortgage_due?
end

You can mock the time for a set of tests easily via setup/teardown methods

describe "some set of tests to mock" do
  before do
    Timecop.freeze(Time.local(1990))
  end

  after do
    Timecop.return
  end

  it "should do blah blah blah" do
  end
end

Set the time for the test environment of a rails app -- this is particularly helpful if your whole application is time-sensitive. It allows you to build your test data at a single point in time, and to move in/out of that time as appropriate (within your tests)

in config/environments/test.rb

config.after_initialize do
  # Set Time.now to September 1, 2008 10:05:00 AM (at this instant), but allow it to move forward
  t = Time.local(2008, 9, 1, 10, 5, 0)
  Timecop.travel(t)
end

The difference between Timecop.freeze and Timecop.travel

freeze is used to statically mock the concept of now. As your program executes, Time.now will not change unless you make subsequent calls into the Timecop API. travel, on the other hand, computes an offset between what we currently think Time.now is (recall that we support nested traveling) and the time passed in. It uses this offset to simulate the passage of time. To demonstrate, consider the following code snippets:

new_time = Time.local(2008, 9, 1, 12, 0, 0)
Timecop.freeze(new_time)
sleep(10)
new_time == Time.now # ==> true

Timecop.return # "turn off" Timecop
Timecop.travel(new_time)
sleep(10)
new_time == Time.now # ==> false

Timecop.scale

Let's say you want to test a "live" integration wherein entire days could pass by in minutes while you're able to simulate "real" activity. For example, one such use case is being able to test reports and invoices that run in 30 day cycles in very little time, while also being able to simulate activity via subsequent calls to your application.

# seconds will now seem like hours
Timecop.scale(3600)
Time.now
# => 2012-09-20 21:23:25 -0500
# seconds later, hours have passed and it's gone from 9pm at night to 6am in the morning
Time.now
# => 2012-09-21 06:22:59 -0500

See #42 for more information, thanks to Ken Mayer, David Holcomb, and Pivotal Labs.

Timecop.safe_mode

Safe mode forces you to use Timecop with the block syntax since it always puts time back the way it was. If you are running in safe mode and use Timecop without the block syntax Timecop::SafeModeException will be raised to tell the user they are not being safe.

# turn on safe mode
Timecop.safe_mode = true

# check if you are in safe mode
Timecop.safe_mode?
# => true

# using method without block
Timecop.freeze
# => Timecop::SafeModeException: Safe mode is enabled, only calls passing a block are allowed.

Rails v Ruby Date/Time libraries

Sometimes Rails Date/Time methods don't play nicely with Ruby Date/Time methods.

Be careful mixing Ruby Date.today with Rails Date.tomorrow / Date.yesterday as things might break.

Contribute

timecop is maintained by travisjeffery, and was created by jtrupiano.

Here's the most direct way to get your work merged into the project.

  • Fork the project
  • Clone down your fork
  • Create a feature branch
  • Hack away and add tests, not necessarily in that order
  • Make sure everything still passes by running tests
  • If necessary, rebase your commits into logical chunks without errors
  • Push the branch up to your fork
  • Send a pull request for your branch

timecop's People

Contributors

andyjdavis avatar ballcheck avatar cgunther avatar dependabot[bot] avatar grosser avatar jamiemccarthy avatar joshuacronemeyer avatar jtrupiano avatar jwillemsen avatar kwerle avatar lmarburger avatar micahchalmer avatar mishina2228 avatar mlarraz avatar mpospelov avatar mschulkind avatar nard-tech avatar nerdrew avatar olleolleolle avatar pdpol avatar petergoldstein avatar ptarjan avatar ptrela avatar tkareine avatar tom-lord avatar travisjeffery avatar uge-developer avatar wishdev avatar yaauie avatar ytkg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

timecop's Issues

UTC and local time in 0.3.4

Following describes problem I had with Timecop. Fix was to revert to 0.3.1.

Struggling with Timecop and Cucumber. In my env.rb I have

require "timecop"

In my hooks I have

Before('@time') do
  puts "1. Before('@time') #{Time.now}" # outputs local time e.g.18:34
  Timecop.freeze Time.now
  sleep(2)
  puts "2. Before('@time') #{Time.now}" # outputs local time e.g.18:34
end

After('@time') do
  puts "After('@time') #{Time.now}" # outputs UTC time e.g. 17:34
  puts Timecop.return
  puts Time.now # outputs local time after features have finished e.g. 18:36
end

Time.now inside a feature tagged with @time is UTC time
Time.now inside a feature not tagged with @time is local time

I'm really confused, wonder if anyone can help or has similar issues

Cheers

Andrew
cucumber (0.6.4, 0.6.3)
cucumber-rails (0.3.0)
timecop (0.3.4)

Doesn't work with FactoryGirl

Perhaps this may be user error but I have the following factory:

Factory.define :auction do |f|
  f.association :product
  f.ending_at 1.hour.from_now
end

In a test, I then tried the following code:

Timecop.freeze do
  @time = 1.hour.from_now
  @auction = Factory.create :auction
end

@time # returns Sat, 01 Jan 2000 01:00:00 PST -08:00
@auction.ending_at # returns Wed, 25 May 2011 20:48:09 PDT -07:00

I expected the two times to be the same.

Date.strptime is not fooled when given an ambiguous date

Example (in Ruby 1.9.3):

irb(main):001:0> require 'timecop'
=> true
irb(main):002:0> ninetyseven = Date.parse('June 1997')
=> #<Date: 1997-06-01 ((2450601j,0s,0n),+0s,2299161j)>
irb(main):003:0> Timecop.freeze(ninetyseven)
=> 1997-06-01 01:00:00 +0100
irb(main):004:0> Date.strptime('March 23', '%b %d')
=> #<Date: 2012-03-23 ((2456010j,0s,0n),+0s,2299161j)>

I would guess the result is in today's year (2012 rather than 1997) because strptime is a C function. I started thinking you could correct the year to Timecop's frozen time under certain circumstances, but it has yak-shave potential.

I'm not highly bothered by this, but thought it was an interesting problem worth raising nonetheless. Feel free to close the issue if there is no easy solution, but it may at least be an idea to include a disclaimer about this behaviour in the README.

Ruby 1.8: 0.5.5 -> 0.5.6 breaks simple test

Got a simple test case that works with timecop version 0.5.5, but not with newer versions under Rails 3.2.10 and Ruby 1.8.
The same test works with Ruby 1.9.

     Failure/Error: Timecop.freeze(2011,1,23) do
     TypeError:
       can't convert ActiveSupport::TimeWithZone into time
Timecop.freeze(2011,1,23) do
  TimeCalc.numeration_till_now(:days).should == (1..23).to_a
end 

Timing issue under 1.9.2-rc1 and busy CPU

Running the Vanity test suite under Ruby 1.8.7, 1.9.1 and 1.9.2-rc1 on my MBP, no issues. But on the CI box, under Ruby 1.9.2-rc1, I get this issue:

ruby-1.9.2-rc1 > Timecop.travel(Date.today - 1) { puts Time.now }
2010-07-09 00:00:00 -0700
 => 2010-07-10 14:15:11 -0700 
ruby-1.9.2-rc1 > Timecop.travel(Date.today - 1) { puts Time.now }
2010-07-08 23:59:59 -0700
 => 2010-07-10 14:15:12 -0700 
ruby-1.9.2-rc1 > Timecop.travel(Date.today - 1) { puts Time.now }
2010-07-08 23:59:59 -0700
 => 2010-07-10 14:15:13 -0700 
ruby-1.9.2-rc1 > Timecop.travel(Date.today - 1) { puts Time.now }
2010-07-09 00:00:00 -0700
 => 2010-07-10 14:15:14 -0700

Notice how, half the time it travels to the right day, the other half, a second into the day before. Those were all executed a second apart in the same IRB session. I get this consistently under 1.9.2-rc1, but no issue under 1.8.7 or 1.9.1. Also, no issue when I use Timecop.freeze.

Independently of the Ruby version, CI is running Ubuntu 8.04 and has quite the workload going on.

Timecop does not handle DateTime nanoseconds properly

I don't think this is the same issue as #70 but forgive me if this is a duplicate.

It appears that Timecop does not preserve nanoseconds in DateTime, so subsequent comparisons with DateTime.now fail.

Context:
Mac OS X 10.6.8, Ruby 1.9.3p362, Timecop 0.5.9.2

Given this file:

require 'spec_helper'
describe Timecop do
  it 'verifies that now is now' do
    now = DateTime.now
    begin
      Timecop.freeze(now)
      DateTime.now.should == now
    ensure
      Timecop.return
    end
  end
end

I get the results:

Failures:

  1) Timecop verifies that now is now
     Failure/Error: DateTime.now.should == now
       expected: #<DateTime: 2013-03-04T08:21:49-08:00 ((2456356j,58909s,535056000n),-28800s,2299161j)>
            got: #<DateTime: 2013-03-04T08:21:49-08:00 ((2456356j,58909s,0n),-28800s,2299161j)> (using ==)
       Diff:
       @@ -1,2 +1,2 @@
       -#<DateTime: 2013-03-04T08:21:49-08:00 ((2456356j,58909s,535056000n),-28800s,2299161j)>
       +#<DateTime: 2013-03-04T08:21:49-08:00 ((2456356j,58909s,0n),-28800s,2299161j)>
     # ./spec/models/timecop_spec.rb:7:in `block (2 levels) in <top (required)>'

undefined method `to_r' for 61200.0:Float - in Ruby 1.8.7

I get the following error in ree 1.8.7 using Timecop 0.4.4 (or 0.4.1) and Rails 3.0. If I go back to Timecop 0.3.5 it works. It also works if I update to Ruby 1.9.3 (which unfortunately isn't an option for me at the moment). Have also tested this in a different Rails 3.2 project with the same results.

>> Timecop.freeze(Time.parse("2011-12-10 13:00-0400")) { puts DateTime.now }
NoMethodError: undefined method `to_r' for 61200.0:Float
    from /Users/sean/.rvm/rubies/ree-1.8.7-2012.02/lib/ruby/1.8/date.rb:527:in `time_to_day_fraction'
    from /Users/sean/.rvm/rubies/ree-1.8.7-2012.02/lib/ruby/1.8/date.rb:707:in `valid_time?'
    from /Users/sean/.rvm/rubies/ree-1.8.7-2012.02/lib/ruby/1.8/date.rb:1521:in `new'
    from /Users/sean/.rvm/gems/ree-1.8.7-2012.02/gems/timecop-0.4.1/lib/timecop/time_stack_item.rb:64:in `datetime'
    from /Users/sean/.rvm/gems/ree-1.8.7-2012.02/gems/timecop-0.4.1/lib/timecop/time_extensions.rb:53:in `mock_time'
    from /Users/sean/.rvm/gems/ree-1.8.7-2012.02/gems/timecop-0.4.1/lib/timecop/time_extensions.rb:66:in `now'
    from (irb):1
    from /Users/sean/.rvm/gems/ree-1.8.7-2012.02/gems/timecop-0.4.1/lib/timecop/timecop.rb:109:in `travel'
    from /Users/sean/.rvm/gems/ree-1.8.7-2012.02/gems/timecop-0.4.1/lib/timecop/timecop.rb:49:in `send'
    from /Users/sean/.rvm/gems/ree-1.8.7-2012.02/gems/timecop-0.4.1/lib/timecop/timecop.rb:49:in `freeze'
    from (irb):1
>> Timecop.freeze(Time.parse("2011-12-10 13:00-0400")) { puts Time.now }
2011-12-10 17:00:00 UTC

timecop should reset global settings before defining each test

This is not a bug in Timecop per se but a usability issue which may cause a few lost hours when it happens. Ideally you ensure Timecop.return to be called as part of teardown so that each new test gets pristine times/dates.

However, since in ruby a test can get created as part of executing a piece of code (shoulda does this as part of defining contexts, setup, teardown etc.), there is a significant possibility for somebody to freeze a time in once of their test files and all tests that get defined after this file inherit the frozen time. For example, consider following piece of code inside a test class:

class TestMyModel < Test::Unit::TestCase
(20 .. 26).each do |day|
Timecop.freeze(Date.parse("April #{day}, 2009"))
should "be valid for #{Date.today.to_s(:header)}" do
Timecop.freeze(Date.parse("April #{day}, 2009"))
@time_sheet.valid_timesheet_for_today?
assert @time_sheet.valid_timesheet_for_today?
end
end
end

In above code, Timecop.freeze gets called while the tests are being created. A quick fix would be to call Timecop.return inside the block after the test is defined. But, we can do better. We patch Shoulda so that Timecop.return gets called after each test is defined. Following is one way to do this:

module Thoughtbot
module Shoulda
class Context
unless method_defined?(:merge_block_with_timecop_return)
def merge_block_with_timecop_return(_args, &blk)
merge_block_without_timecop_return(_args, &blk)
Timecop.return
end
alias_method_chain :merge_block, :timecop_return
end
end
end
end

I am assuming, necessary guards can be put around this so it is a no-op for folks not using Shoulda. There must be a similar timecop-reset that could be defined for folks using old-fashioned def test_x; end; type of constructs.

Timecop handles GMT/UTC Time objects incorrectly

Timecop always calls Time#getlocal regardless if the time object passed to parse_time() is GMT or UTC.

If the passed in object is an instance of Time, why not just use it's values directly? Why call getlocal? Besides, for large scale distributed applications, each machine could be in different timezones, causing getlocal to return different values! (Granted, for mocking and testing, this scenario isn't likely.)

Time zone problems with 0.5.6

Hi,

I'm experiencing time zone problems with new timecop version, 0.5.6:

[36] pry(main)> t=Time.zone.parse("2012-12-27T12:12:12+08:00")
=> Thu, 27 Dec 2012 12:12:12 SGT +08:00
[37] pry(main)> Timecop.freeze(t) { Time.zone.now }
=> Thu, 27 Dec 2012 20:12:12 SGT +08:00

I used 0.5.3 before, and this same code worked as it should:

[2] pry(main)> t=Time.zone.parse("2012-12-27T12:12:12+08:00")
=> Thu, 27 Dec 2012 12:12:12 SGT +08:00
[3] pry(main)> Timecop.freeze(t) { Time.zone.now }
=> Thu, 27 Dec 2012 12:12:12 SGT +08:00

Timecop defaults to 2012-01-01 00:00:00 when a string is passed

Timecop is incredibly useful and we have it in a lot of tests. Recently I discovered that it doesn't always behave as I would want.

I would expect Timecop.freeze or Timecop.travel to either raise an error or possibly call Time.parse when passed a string. Instead, it simply sets the current time to the beginning of the current year. This is surprising and I think it
irb(main):003:0> Timecop.travel('2012-02-28') => 2012-01-01 00:00:00 +0000 irb(main):004:0> Time.now => 2012-01-01 00:00:02 +0000
I'm happy to write the code for fixing this, but I'm trying to understand if there is a reason for this behavior before doing so.

named_scope seems to not work?

I am not totally sure whether I'm just doing something wrong or whether this is a bug, but I'm trying to find Order objects which need to be delivered on Date.today, with a named_scope like this:

  named_scope :for_today,
              :include => [:delivery => :delivery_slot],
              :conditions => ['delivery_slots.date = ?', Date.today],
              :order => 'delivery_slots.timeslot'

and in a console session:

  >> Order.for_today.count  
  => 9
  >> require 'timecop'
  => []
  >> Timecop.travel(Date.today + 1) do 
  ?> Order.for_today.count 
  >> end
  => 9
  >> Timecop.travel(Date.today + 5) do 
  ?> Order.for_today.count
  >> end
  => 9
  >> Timecop.travel(Date.today + 5) do 
  ?> puts Date.today
  >> end
  2009-11-11 # <= this is correct as right now it's 6 Nov.
  => nil
  >> Timecop.travel(Date.today + 500) do 
  ?> Order.for_today.count
  >> end
  => 9
  >> quit

There are definitely no orders which have an associated Delivery which is 500 days in the future, so I'm wondering whether this is perhaps a bug with named_scope?

v0.5.4 fails to load under spork, reverting to v0.5.3 works

OS: Mac Mountain Lion 10.8.2
Ruby: 1.9.3p194
Rails: 3.2.8
Spork: 0.9.2
rvm: 1.14.10

  • verified that gem is loaded in:
    /Users/me/.rvm/gems/ruby-1.9.3-p194/gems/timecop-0.5.4/
  • switching back to v0.5.3 and all works as expected

$ spork
Using RSpec
Preloading Rails environment
cannot load such file -- timecop/time_extensions (LoadError)
/Users/me/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:251:in require' /Users/me/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:251:inblock in require'
/Users/me/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:236:in load_dependency' /Users/me/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:251:inrequire'
/Users/me/.rvm/gems/ruby-1.9.3-p194/gems/timecop-0.5.4/lib/timecop/timecop.rb:2:in <top (required)>' /Users/me/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:251:inrequire'
/Users/me/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:251:in block in require' /Users/me/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:236:inload_dependency'
/Users/me/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:251:in require' /Users/me/.rvm/gems/ruby-1.9.3-p194/gems/timecop-0.5.4/lib/timecop.rb:1:in<top (required)>'
/Users/me/.rvm/gems/ruby-1.9.3-p194/gems/bundler-1.2.0/lib/bundler/runtime.rb:68:in require' /Users/me/.rvm/gems/ruby-1.9.3-p194/gems/bundler-1.2.0/lib/bundler/runtime.rb:68:inblock (2 levels) in require'
/Users/me/.rvm/gems/ruby-1.9.3-p194/gems/bundler-1.2.0/lib/bundler/runtime.rb:66:in each' /Users/me/.rvm/gems/ruby-1.9.3-p194/gems/bundler-1.2.0/lib/bundler/runtime.rb:66:inblock in require'
/Users/me/.rvm/gems/ruby-1.9.3-p194/gems/bundler-1.2.0/lib/bundler/runtime.rb:55:in each' /Users/me/.rvm/gems/ruby-1.9.3-p194/gems/bundler-1.2.0/lib/bundler/runtime.rb:55:inrequire'
/Users/me/.rvm/gems/ruby-1.9.3-p194/gems/bundler-1.2.0/lib/bundler.rb:128:in require' /Users/me/dev/rails_projects/codefied/houseqall-web/config/application.rb:13:in<top (required)>'
/Users/me/.rvm/gems/ruby-1.9.3-p194/gems/spork-0.9.2/lib/spork/app_framework/rails.rb:48:in require' /Users/me/.rvm/gems/ruby-1.9.3-p194/gems/spork-0.9.2/lib/spork/app_framework/rails.rb:48:inpreload_rails'
/Users/me/.rvm/gems/ruby-1.9.3-p194/gems/spork-0.9.2/lib/spork/app_framework/rails.rb:7:in preload' /Users/me/.rvm/gems/ruby-1.9.3-p194/gems/spork-0.9.2/lib/spork/test_framework.rb:134:inblock in preload'
/Users/me/.rvm/gems/ruby-1.9.3-p194/gems/spork-0.9.2/lib/spork.rb:62:in exec_prefork' /Users/me/.rvm/gems/ruby-1.9.3-p194/gems/spork-0.9.2/lib/spork/test_framework.rb:120:inpreload'
/Users/me/.rvm/gems/ruby-1.9.3-p194/gems/spork-0.9.2/lib/spork/run_strategy/forking.rb:25:in preload' /Users/me/.rvm/gems/ruby-1.9.3-p194/gems/spork-0.9.2/lib/spork/runner.rb:74:inrun'
/Users/me/.rvm/gems/ruby-1.9.3-p194/gems/spork-0.9.2/lib/spork/runner.rb:10:in run' /Users/me/.rvm/gems/ruby-1.9.3-p194/gems/spork-0.9.2/bin/spork:10:in<top (required)>'
/Users/me/.rvm/gems/ruby-1.9.3-p194/bin/spork:19:in load' /Users/me/.rvm/gems/ruby-1.9.3-p194/bin/spork:19:in

'
/Users/me/.rvm/gems/ruby-1.9.3-p194/bin/ruby_noexec_wrapper:14:in eval' /Users/me/.rvm/gems/ruby-1.9.3-p194/bin/ruby_noexec_wrapper:14:in'

TimeCop handles timezones incorrectly

I am not sure why this issue was closed but I am following up with the suggestion to allow for handling of UTC dates. If this cannot happen by default, perhaps it can be done with an option of sorts.

In our case, the current logic leads to specs that pass during the day and fail during the night.

Here is an example:

it 'returns all reports generated on a certain date' do
    Timecop.freeze 1.day.ago do
      create_list :report, 4, name: '1.day.ago'
    end

    Report.generated_on(1.day.ago).count.should == 4
end

The intent of the spec couldn't be clearer. It is difficult for me to agree with any default logic in Timecop that leads to inconsistent behavior in such a simple case.

Tests are failing with Ruby1.8

Running tests for ruby1.8 using debian/ruby-tests.rb...
Loaded suite debian/ruby-tests
Started

...........E

Error: test_freeze_with_date_instance_works_as_expected(TestTimecop)
NoMethodError: private method to_datetime' called for Thu Nov 01 13:43:51 -0500 2012:Time ./debian/ruby-timecop//usr/lib/ruby/vendor_ruby/timecop/time_extensions.rb:60:innow_without_mock_time'
57: end
58:
59: def now_without_mock_time
=> 60: Time.now_without_mock_time.to_datetime
61: end
62:
63: def now_with_mock_time
./test/test_helper.rb:24:in local_offset' ./test/timecop_test.rb:180:intest_freeze_with_date_instance_works_as_expected'
./debian/ruby-timecop//usr/lib/ruby/vendor_ruby/timecop/timecop.rb:132:in travel' ./debian/ruby-timecop//usr/lib/ruby/vendor_ruby/timecop/timecop.rb:52:insend'
./debian/ruby-timecop//usr/lib/ruby/vendor_ruby/timecop/timecop.rb:52:in freeze' ./test/timecop_test.rb:177:intest_freeze_with_date_instance_works_as_expected'

/usr/lib/ruby/vendor_ruby/mocha/integration/test_unit/gem_version_230_to_240.rb:25:in `run'

....E

Error: test_freeze_with_integer_instance_works_as_expected(TestTimecop)
NoMethodError: private method to_datetime' called for Thu Nov 01 13:43:51 -0500 2012:Time ./debian/ruby-timecop//usr/lib/ruby/vendor_ruby/timecop/time_extensions.rb:60:innow_without_mock_time'
57: end
58:
59: def now_without_mock_time
=> 60: Time.now_without_mock_time.to_datetime
61: end
62:
63: def now_with_mock_time
./test/test_helper.rb:24:in local_offset' ./test/timecop_test.rb:191:intest_freeze_with_integer_instance_works_as_expected'
./debian/ruby-timecop//usr/lib/ruby/vendor_ruby/timecop/timecop.rb:132:in travel' ./debian/ruby-timecop//usr/lib/ruby/vendor_ruby/timecop/timecop.rb:52:insend'
./debian/ruby-timecop//usr/lib/ruby/vendor_ruby/timecop/timecop.rb:52:in freeze' ./test/timecop_test.rb:189:intest_freeze_with_integer_instance_works_as_expected'

/usr/lib/ruby/vendor_ruby/mocha/integration/test_unit/gem_version_230_to_240.rb:25:in `run'

.E

Error: test_freeze_with_time_instance_works_as_expected(TestTimecop)
NoMethodError: private method to_datetime' called for Thu Nov 01 13:43:51 -0500 2012:Time ./debian/ruby-timecop//usr/lib/ruby/vendor_ruby/timecop/time_extensions.rb:60:innow_without_mock_time'
57: end
58:
59: def now_without_mock_time
=> 60: Time.now_without_mock_time.to_datetime
61: end
62:
63: def now_with_mock_time
./test/test_helper.rb:24:in local_offset' ./test/timecop_test.rb:117:intest_freeze_with_time_instance_works_as_expected'
./debian/ruby-timecop//usr/lib/ruby/vendor_ruby/timecop/timecop.rb:132:in travel' ./debian/ruby-timecop//usr/lib/ruby/vendor_ruby/timecop/timecop.rb:52:insend'
./debian/ruby-timecop//usr/lib/ruby/vendor_ruby/timecop/timecop.rb:52:in freeze' ./test/timecop_test.rb:115:intest_freeze_with_time_instance_works_as_expected'

/usr/lib/ruby/vendor_ruby/mocha/integration/test_unit/gem_version_230_to_240.rb:25:in `run'

.....E

Error: test_mocked_date_time_now_is_local(TestTimecop)
NoMethodError: private method to_datetime' called for Thu Nov 01 13:43:51 -0500 2012:Time ./debian/ruby-timecop//usr/lib/ruby/vendor_ruby/timecop/time_extensions.rb:60:innow_without_mock_time'
57: end
58:
59: def now_without_mock_time
=> 60: Time.now_without_mock_time.to_datetime
61: end
62:
63: def now_with_mock_time
./test/test_helper.rb:24:in local_offset' ./test/timecop_test.rb:246:intest_mocked_date_time_now_is_local'
./debian/ruby-timecop//usr/lib/ruby/vendor_ruby/timecop/timecop.rb:132:in travel' ./debian/ruby-timecop//usr/lib/ruby/vendor_ruby/timecop/timecop.rb:52:insend'
./debian/ruby-timecop//usr/lib/ruby/vendor_ruby/timecop/timecop.rb:52:in freeze' ./test/timecop_test.rb:245:intest_mocked_date_time_now_is_local'
./test/test_helper.rb:35:in each_timezone' ./test/test_helper.rb:33:ineach'
./test/test_helper.rb:33:in each_timezone' ./test/timecop_test.rb:243:intest_mocked_date_time_now_is_local'

/usr/lib/ruby/vendor_ruby/mocha/integration/test_unit/gem_version_230_to_240.rb:25:in `run'

.................

Finished in 0.550921 seconds.

42 tests, 88 assertions, 0 failures, 4 errors, 0 pendings, 0 omissions, 0 notifications
90.4762% passed

76.24 tests/s, 159.73 assertions/s

Test "ruby1.8" failed. Continue building the package? (Y/N) y
/usr/bin/ruby1.9.1 -I/usr/lib/ruby/vendor_ruby /usr/lib/ruby/vendor_ruby/gem2deb/test_runner.rb
Running tests for ruby1.9.1 using debian/ruby-tests.rb...
Loaded suite debian/ruby-tests
Started
..........................................

Finished in 0.557973704 seconds.

42 tests, 109 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed

75.27 tests/s, 195.35 assertions/s
Leaving dh_ruby --install
dh_install -O--buildsystem=ruby
dh_installdocs -O--buildsystem=ruby
dh_installchangelogs -O--buildsystem=ruby
dh_installexamples -O--buildsystem=ruby
dh_installman -O--buildsystem=ruby
dh_installcatalogs -O--buildsystem=ruby
dh_installcron -O--buildsystem=ruby
dh_installdebconf -O--buildsystem=ruby
dh_installemacsen -O--buildsystem=ruby
dh_installifupdown -O--buildsystem=ruby
dh_installinfo -O--buildsystem=ruby
dh_pysupport -O--buildsystem=ruby
dh_pysupport: This program is deprecated, you should use dh_python2 instead. Migration guide: http://deb.li/dhs2p
dh_installinit -O--buildsystem=ruby
dh_installmenu -O--buildsystem=ruby
dh_installmime -O--buildsystem=ruby
dh_installmodules -O--buildsystem=ruby
dh_installlogcheck -O--buildsystem=ruby
dh_installlogrotate -O--buildsystem=ruby
dh_installpam -O--buildsystem=ruby
dh_installppp -O--buildsystem=ruby
dh_installudev -O--buildsystem=ruby
dh_installwm -O--buildsystem=ruby
dh_installxfonts -O--buildsystem=ruby
dh_installgsettings -O--buildsystem=ruby
dh_bugfiles -O--buildsystem=ruby
dh_ucf -O--buildsystem=ruby
dh_lintian -O--buildsystem=ruby
dh_gconf -O--buildsystem=ruby
dh_icons -O--buildsystem=ruby
dh_perl -O--buildsystem=ruby
dh_usrlocal -O--buildsystem=ruby
dh_link -O--buildsystem=ruby
dh_compress -X.rb -O--buildsystem=ruby
dh_fixperms -O--buildsystem=ruby
dh_installdeb -O--buildsystem=ruby
dh_gencontrol -O--buildsystem=ruby
dpkg-gencontrol: warning: Depends field of package ruby-timecop: unknown substitution variable ${shlibs:Depends}
dh_md5sums -O--buildsystem=ruby
dh_builddeb -O--buildsystem=ruby

Timezone problem when calling Timecop.freeze at v0.5.8

At v0.5.8, Timecop.freeze has timezone issue (similar to #57)

[55] pry(main)> require 'timecop'
[57] pry(main)> require 'active_support/all'                                                                        

[58] pry(main)> Time.zone
=> (GMT+09:00) Tokyo
[59] pry(main)> Time.now
=> 2013-01-09 22:28:16 +0900
[60] pry(main)> Timecop.freeze(Time.now)
=> 2013-01-10 07:28:16 +0900

It seems that 68ec6ad occurs this issue.
I think the behaviour removed at 68ec6ad#L0R60 was neccessary.

Since it's the timezone matter problem, running rake test also failed on my laptop. My timezone is Asia/Tokyo (+09:00).

Run options: 

# Running tests:

.......................F..

Finished tests in 0.342019s, 76.0192 tests/s, 181.2765 assertions/s.

  1) Failure:
test_timezones(TestTimeStackItem) [time_stack_item_test.rb:191]:
<Thu, 27 Dec 2012 05:12:12 CET +01:00> expected but was
<2012-12-27 14:12:12 +0900>.

26 tests, 62 assertions, 1 failures, 0 errors, 0 skips
Run options: 

# Running tests:

...........................................

Finished tests in 0.525874s, 81.7686 tests/s, 209.1756 assertions/s.

43 tests, 110 assertions, 0 failures, 0 errors, 0 skips
Run options: 

# Running tests:

.

Finished tests in 0.007542s, 132.5908 tests/s, 132.5908 assertions/s.

1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
Run options: 

# Running tests:

........

Finished tests in 0.505886s, 15.8138 tests/s, 59.3019 assertions/s.

8 tests, 30 assertions, 0 failures, 0 errors, 0 skips

v0.4 Time returns wrong type

require 'timecop'
Timecop.freeze(Time.now) { puts Time.now.class } #=> ActiveSupport::TimeWithZone

This is not how AS::TimeWithZone works, and some libraries, like the MongoDB driver, actively exclude TimeWithZone, meaning code that works now raises under test, for example: BSON::InvalidDocument: ActiveSupport::TimeWithZone is not currently supported; use a UTC Time instance instead.

License

Please update your license to reflect the Copyright holder. Currently it is blank.

test_time_stack_item testsuite fails

Running test_time_stack_item...
Loaded suite test_time_stack_item
Started
....F...........
Finished in 0.102263 seconds.

  1. Failure:
    test_datetime_for_dst_to_non_dst(TestTimeStackItem) [test_time_stack_item.rb:139]:
    <#<Date: 4910229/2,0,2299161>> expected but was
    <#<Date: 4910231/2,0,2299161>>.

16 tests, 59 assertions, 1 failures, 0 errors
FAILED!!!!!!!!!!!!

Time.new shows different time than Time.now

... and the same is for DateTime. Sample irb session

ruby-1.8.7-p330 ~/Documents/git/catcher (master) $  irb 
pry(#<Object:0x1001dd2a0>)> require "timecop"
=> true
pry(#<Object:0x1001dd2a0>)> Timecop.freeze(DateTime.parse("2010-10-10T10:10:10.100+0200"))
=> Sun Oct 10 09:10:10 +0200 2010
pry(#<Object:0x1001dd2a0>)> Time.new
=> Mon Oct 31 15:01:25 +0100 2011
pry(#<Object:0x1001dd2a0>)> Time.now
=> Sun Oct 10 09:10:10 +0200 2010
pry(#<Object:0x1001dd2a0>)> DateTime.new.zone
=> "+00:00"
pry(#<Object:0x1001dd2a0>)> DateTime.now.zone
=> "+01:00"
pry(#<Object:0x1001dd2a0>)> ^D
ruby-1.8.7-p330 ~/Documents/git/catcher (master) $  gem li timecop

*** LOCAL GEMS ***

timecop (0.3.5)
ruby-1.8.7-p330 ~/Documents/git/catcher (master) $

A Timecop.freeze with a block call returns to 2nd to last date instead of date before the block call

Ran into this while writing some rspec tests:

require 'spec_helper'

describe 'Timecop bug' do
  around do |example|
    date = Date.today
    Timecop.freeze(Date.new(2001, 1, 15)) do
      example.run
    end
    expect(Date.today).to eql(date)
  end

  it "calls Timecop.freeze some more" do
    Timecop.freeze(Date.new(2001, 1, 16))
    Timecop.freeze(Date.new(2001, 1, 17))
    Timecop.freeze(Date.new(2001, 1, 18))
  end
end

Result:

F

Failures:

  1) Timecop bug calls Timecop.freeze some more
     Failure/Error: expect(Date.today).to eql(date)

       expected: Thu, 28 Feb 2013
            got: Wed, 17 Jan 2001

       (compared using eql?)

       Diff:
       @@ -1,2 +1,2 @@
       -Thu, 28 Feb 2013
       +Wed, 17 Jan 2001

     # ./spec/time_cop_bug_spec.rb:9

Finished in 1.94 seconds
1 example, 1 failure

Failed examples:

rspec ./spec/time_cop_bug_spec.rb:12 # Timecop bug calls Timecop.freeze some more

Date.today reports wrong date when frozen

Today is 2013, 1, 8 but when I freeze the date for yesterday it reports the wrong date. Running ruby 1.9.3 and Timecop 0.5.8.

require 'timecop'
require 'date'

Timecop.freeze(2013,1,7) do
  puts Date.today
  puts Time.now
end

I expected to see:

2013-01-07
2013-01-07 00:00:00 -0500

But, I actually see:

2013-01-08
2013-01-07 00:00:00 -0500

Am I using this incorrectly?

NoMethodError in 0.4.5

Failure/Error: Timecop.freeze(Date.new(2012, 6, 9)) do
NoMethodError:
undefined method `local' for nil:NilClass

But this code works in 0.3.5.

Time Scoping Issue

I'm having a slight issue with one of my gems where I want to have my own Time class which has logic independent to my application.

Currently I believe that, depending on load order, Timecop overrides any Time class' behavior and not just Ruby's base Time class (eg MyApplication::Time).

I may be wrong, but I think that simply adding a double-colon to the class Time line will fix the problem. Like so:

class ::Time

I didn't fix it because I wasn't sure if this was desired behavior or not.

Include cucumber steps

I think the main area of application for this gem is in BDD/TDD, so it could really benefit from having some "batteries included" for the usual suspects.
It would be cool to have some predefined steps accessible (like those in https://gist.github.com/154616) by require 'timecop/cucumber'.
WDYT?

Timecop should automaticaly handle returning time back to normal

I just tracked down a bug in our tests where intermittent failures were occurring (really scared to see how many fail when I fix this bug). I got burned by the fact that Timecop doesn't always return time back to normal.

Example:
Timecop.freeze { Timecop.travel(1.second) }

After running that, all subsequent tests will have Time.now frozen (curiously to 2000-1-1). From reading the api again, I see there's the Timecop.return method that will fix this issue for us.

2 issues/opinions:

  1. when that block closes timecop should see there are no more time blocks around, and reset time back to normal right away
  2. after the test with that line finishes, it seems like a disaster waiting to happen for that time to not be automatically reset back to normal

For 2 I think if possible Timecop should be managing resetting that. All other mocking libraries out there would implicitly handle that, so it seems fair for a user to assume Timecop would as well.

If that's not possible, or overly complicated it seems deserving of a big, flashing, red section of the Readme that says "watch out, you better add an after(:each) hook which calls Timecop.return or your time will be frozen for-ev-er"

Rubyzip crash when freezing time

In a Cucumber feature file, I'm freezing the time and then generate a zip file using rubyzip.

Here is my function to generate the zip file:

Zip::ZipOutputStream.open(path) do |zip|
  filelist.each do |file|
    zip.put_next_entry(File.basename(file))
    zip.print IO.read(file)
  end
end

It fail with the following exception message:

undefined method `to_binary_dos_time' for Sun Apr 01 12:34:56 +0200 2012:Time

This Time is the one used for freeze in my feature file.

The issue is that rubyzip initialize a new Zip::ZipEntry.new in the method put_next_entry where the initialize method receive some arguments with one named time initialized with the rubyzip class Zip::DOSTime.

When I use my custom method to generate zip without using cucumber step to freeze time, there is no issue, but when I freeze the time, as far as I understood, timecop use the class TimeStackItem and then there is no to_binary_dos_time method.

I don't know how could it be solved... but it is very annoying.

Timecop.freeze with no args on 1.9

While starting to upgrade gemcutter to 1.9, I came across Timecop.freeze with no arguments. That resulted in this timestamp: 0000-01-01 00:00:00 -0500 . Not sure if this just old API or a bug

Nanoseconds are not saved in Ruby1.9.3

Here it the test which detects the bug:

    def test_save_nanoseconds_in_ruby19
      t = Time.now
      Timecop.freeze t
      assert_equal t.nsec, Time.now.nsec
    end

It fails:

test_save_nanoseconds_in_ruby19(TestTimecop) [./timecop_test.rb:444]:
<801912157> expected but was
<801912069>.

Actually it leads to situation when:

t = Time.now
Timecop.freeze t
Time.now == t # => false

Thanks.

Doesn't work Date.yesterday and Date.tomorrow with Rails 3.1

Timecop returns wrong result

pry(main)> [Date.yesterday, Date.today, Date.tomorrow]
=> [Fri, 28 Oct 2011, Sat, 29 Oct 2011, Sun, 30 Oct 2011]

pry(main)> Timecop.freeze(Date.today + 30)
=> 2011-11-28 00:00:00 +0200

pry(main)> [Date.yesterday, Date.today, Date.tomorrow]
=> [Sat, 26 Nov 2011, Mon, 28 Nov 2011, Mon, 28 Nov 2011]

Environment

ruby-1.9.2-p290
rails (3.1.0)
activesupport (3.1.0)
timecop (0.3.5)

Strange DST issue

I've got a test using Time.now.to_s within a Timecop.freeze block. In the test, the string is hashed and compared to a static value. The test worked fine last week, but today, after DST ended locally, the hash string ends up being a different value and the test failed.

Using Timecop itself to simulate the difference:

# During DST:
Timecop.travel(2012,11,2) do
  Timecop.freeze(DateTime.new(2012,1,1,10,0,0)) do
    Time.now.to_s
  end
end
# => "2012-01-01 06:00:00 -0500"

# After DST:
Timecop.travel(2012,11,5) do
  Timecop.freeze(DateTime.new(2012,1,1,10,0,0)) do
    Time.now.to_s
  end
end
#=> "2012-01-01 05:00:00 -0500"

This seems to be related to using DateTime.new(...) within the Timecop.freeze call, because if I just pass the numbers straight to Timecop.freeze, I get "2012-01-01 10:00:00 -0500" in both cases.

0.4.5 changed timezone comparison behavior

this spec:

Timecop.freeze(@t = Time.now)
expect{
  # code...
}.to change(@object, :attribute).from(nil).to(@t)

used to pass with 0.4.4, but now fails with 0.4.5, with the message:

attribute should have been changed to 2012-08-10 23:10:57 -0400, but is now Sat, 11 Aug 2012 03:10:57 UTC +00:00

If I use pry to set a breakpoint with 0.4.4 and 0.4.5, I observe:

  • @t.to_f and @object.attribute.to_f are indeed identical as they should be
  • the timezones for each object are the same with both versions.

So, my guess: pry is doing its timefreezing job, and the Time objects are the same, but for some reason the comparison is now taking the timezone into consideration where before it was not. Is this expected/possible?

Timecop doesn't persist over simulated page loads in Cucumber tests

I just ran into a problem where I'm using Timecop in a cucumber feature so that I can drop specific dates into my scenarios without having to worry about when I run the feature:

Background:
  Given it is now "March 2 2011"

Scenario: Admin user creates an event
  Given I am logged in as a user (admin)
  When I am on the home page
  And I follow "Add event"
  And I fill in the following:
  | event_timestamp_1i | 2011            |
  | event_timestamp_2i | March           |
  | event_timestamp_3i | 9               |
  | event_description  | The first event |
  And I press "Create Event"
  Then I should have 1 future event
  And I should see "March 9th, 2011" within "#next_event"

However the scenario fails. It seems from experimentation that - after the page load from "Create Event" - the time is reset to the real Time.now and the "future" event is now in the past.

Is there a way around this?

Make freeze/travel return result of the block, instead of current time

Hello,

Would it be possible to change the implementation so the freeze/travel methods return the result of the passed in block, instead of current time? I think that would be more useful, because then you could do something like this:

user = Timecop.freeze(2.years.ago) { User.create!(:name => '...') }

Instead of this:

user = nil
Timecop.freeze(2.years.ago) do
  user  = User.create!(:name => '...')
end

I think that first line is necessary, because otherwise the user variable is lost when it comes out of block's scope.

Timecop has no effect when parsing Dates

When I do the following:

Timecop.travel(Date.parse('Dec 15, 2012')
d = Date.parse('Dec 15')
d == Date.parse('Dec 15, 2013')

parse just assumes it is current real year.

Any way to fix or advice? I wouldn't mind putting in a pull request, though not sure where to start.

Thanks,
Ilya

Causing a segfault on ruby 1.9.3.p0

subj.
sometimes. also often hangs with 100% cpu usage.
seems since i start using .beginning_of_day, .end_of_day.
not sure how to reproduce.

v0.4 can't dup Fixnum

When using the Timecop.freeze(year, month, day, hour=0, minute=0, second=0) argument style and Time responds to parse then a can't dup Fixnum exception is raised because Time.parse expects a String argument.

LoadError: no such file to load -- timecop on 0.5.8 and 0.5.9

Hello I'm getting no such file to load on timecop 0.5.8 and 0.5.9m it works fine on 0.5.7.

Im on OSX mountain lion with ruby 1.8.7

$ gem list | ack timecop
timecop (0.5.9, 0.5.8, 0.5.7, 0.3.5)
☺ aalvarado 15:16:09 ~
$ pry
[1] pry(main)> gem 'timecop', '=0.5.7'
=> true
[2] pry(main)> require 'timecop'
=> true
[3] pry(main)> Timecop.class
=> Class
[4] pry(main)> exit
☺ aalvarado 15:16:41 ~
$ pry
[1] pry(main)> gem 'timecop', '=0.5.8'
=> true
[2] pry(main)> require 'timecop'
LoadError: no such file to load -- timecop
from /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:60:in `gem_original_require'
[3] pry(main)> exit
☺ aalvarado 15:17:51 ~
$ pry
[1] pry(main)> gem 'timecop', '=0.5.9'
=> true
[2] pry(main)> require 'timecop'
LoadError: no such file to load -- timecop
from /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:60:in `gem_original_require'
[3] pry(main)> 

timecop doesnt work for testing web services?

hi,

first of all, i want to thank you for writing such an awesome library. its super useful, but im having a problem right now:

i have a rails test server running on port 3000, and i have specs that test interaction with this test server's api. the created_at field for the objects created through rest calls to the server from my specs don't use timecops time. is this a bug or does timecop not support this scenario?

thx in advance.

Daylight Savings Time issue?

1.9.3p125 :064 > Timecop.return.utc
 => 2012-03-01 23:08:44 UTC 
1.9.3p125 :065 > Timecop.travel(10.days).utc
 => 2012-03-11 22:08:53 UTC 
1.9.3p125 :066 > Timecop.return.utc
 => 2012-03-01 23:08:54 UTC 
1.9.3p125 :067 > Timecop.travel(10 * 86400).utc
 => 2012-03-11 23:08:59 UTC 

Happens in 1.8 as well.

Doesn't happen if I jump 9 days instead of 10. Daylight Savings Time occurs March 11th this year... is there some compensation for it that's occurring in reverse?

Timecop seems to incorrectly freeze ActiveSupport 1.year.from_now

Time.now  #=> 2013-02-01 10:36:54 -0500
Timecop::VERSION #=> "0.5.1"  
Timecop.freeze("01/01/2012")
Time.zone  #=> (GMT-07:00) Mountain Time (US & Canada)
Time.now #=> 2012-01-01 00:00:00 -0500
1.year.from_now #=> Mon, 31 Dec 2012 22:00:00 MST -07:00

why would Timecop freeze at -5 hours and yet return a year from now at -7hours? Not sure if this is a bug or my use of the API, thanks.

Travelling once across DST gives the wrong offset for DateTime

But travelling a second time fixes it.

require 'active_support/core_ext/time/zones'
require 'timecop'

Time.zone = "America/New_York"
jan1 = Time.zone.parse("Jan 1, 2012 10:00AM")
puts jan1
puts

puts "Before:"
puts DateTime.now
puts Time.now
puts

Timecop.travel(jan1)
puts "After 1:"
puts DateTime.now
puts Time.now
puts

Timecop.travel(jan1)
puts "After 2:"
puts DateTime.now
puts Time.now
puts
2012-01-01 10:00:00 -0500

Before:
2012-07-23T12:49:41-04:00
2012-07-23 12:49:41 -0400

After 1:
2012-01-01T10:00:00-04:00
2012-01-01 10:00:00 -0500

After 2:
2012-01-01T10:00:00-05:00
2012-01-01 10:00:00 -0500

After the first travel, the DateTime has the current local offset (-04:00) rather than the offset of the given TimeWithZone. Travelling again mysteriously fixes it.

Notice that only DateTime, not Time, fails.


I see there are a couple of other DST issues around, but I'm not sure if this is a duplicate of any of them.

Timecop does not work as expected within FactoryGirl factories

If you use Timecop within FactoryGirl factories, you get unexpected results, and many created_at times are actually set to current real-time. I've created an example rails project that somewhat illustrates this issue. The book_spec shows some relevant examples.

Example

I have need of a factory that does a bunch of things in the past. I wanted to do this because the factory is needed in a large number of specs, and I wanted to save the time of doing these creation loops in every before filter.

A very simplified and domain-changed example of what I'm doing is in the :published_book factory:

  factory :book do
    sequence(:title){|n|"Book#{n}"}

    factory :published_book do

      ignore do
        days_ago 10
      end

      after :create do |book, evaluator|
        Timecop.travel(evaluator.days_ago) do
          book.update_attribute :created_at, Time.now
          2.times do
            authorship = book.authorships.create(FactoryGirl.build(:authorship, book: book, author: FactoryGirl.create(:author)).attributes.symbolize_keys)
            authorship.save!
          end
        end

        evaluator.days_ago.times.to_a.reverse.each do |i|
          Timecop.travel(i.days.ago) do
            book.authors.each do |author|
              author.write_chapter! book
            end
          end
        end
      end
    end

  end

As you can see, I need to perform functions on the relationship between the authors and books starting some time ago and moving forward. I don't want to perform these functions in every test situation.

Timecop inside the factory fails

This all works well, except that it turns out that using Timecop to create the authorships in the after :create filter doesn't really work (also, the update_attribute call to book has been added for illustration).

Using this factory, the following tests fail:

  before do
    @book = FactoryGirl.create :published_book
  end

  specify {@book.created_at.should be_within(1.hour).of(10.days.ago)}
  specify {@book.authors.first.created_at.should be_within(1.hour).of(10.days.ago)}

Because the creation time of both @book and all authors is current real time, not 10.days.ago as expected

The way it looks, Timecop cannot work well within a FactoryGirl factory and force the creation of models with proper times. Is this expected?

I should note that the following tests pass

  before do
    Timecop.travel(10.days.ago)
        @book = FactoryGirl.create :published_book
    end
  end

  specify {@book.created_at.should be_within(1.hour).of(10.days.ago)}
  specify {@book.authors.first.created_at.should be_within(1.hour).of(10.days.ago)}

However, I'm worried about this because we're sort of avoiding Timecop's stated abilities and going back in time only to go back in time. I'm not sure it's going to screw something else up. To wit: The following fails:


  it "Shouldn't go too far back in time" do

    Timecop.travel(1.day.ago) do
      Timecop.travel(3.days.ago) do
        @oldbook = FactoryGirl.create :book
      end
    end

    @oldbook.created_at.should_not be_within(1.hour).of(4.days.ago)
  end

which suggests that the above workaround is, as I suspected, dangerous.

The only thing I can think to do now is to pull the Timecop stuff out of the factory, which necessitates building this loop code in a separate method. I'm not sure if the problem can actually be fixed, but wanted to know for sure.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.