I am using the timecop gem in order to stub out dates. I am also using factory_girl in order to make creating objects for my specs easier.
Here is my factory:
FactoryGirl.define do
factory :fiscal_year do
start_date {Date.today}
end_date {Date.today + 1.day}
trait(:sequenced_start_date) {sequence(:start_date, Date.today.year) {|n| Date.new(n, 10, 1) }}
trait(:sequenced_end_date) {sequence(:end_date, Date.today.year + 1) {|n| Date.new(n, 9, 30) }}
factory :sequenced_fiscal_year, parent: :fiscal_year,
traits: [:sequenced_start_date, :sequenced_end_date]
end
end
Assume that today's actual date is 09/07/2016.
Here is my spec:
RSpec.describe FiscalYear, type: :model do
before(:example) do
Timecop.freeze(Time.local(2010))
end
after do
Timecop.return
end
describe "fake spec to show unexpected sequence behavior" do
it "tests something" do
puts "Today's frozen date thanks to Timecop: #{Date.today}"
fiscal_year = create(:sequenced_fiscal_year)
puts "Sequenced Date: #{fiscal_year.end_date}"
end
end
end
When the spec is run here is the output:
Today's frozen date thanks to Timecop: 01/01/2010
Sequenced Date: 09/30/2017
In the first output line: Timecop works as expected when I call Date.today
. It has successfully frozen the time and returns that frozen time.
However: the second line does not work as expected. It is clearly using the system Date
as opposed to Timecop's frozen Date
. The sequenced end_date
should sequentially add +1 year to the currently frozen Date. Therefore: I expect the end_date
here to be 09/30/2011
, as opposed to 09/30/2017
.
How can I make it so that the sequenced end_date
attribute goes off of Timecop's Date
as opposed to the system's Date
?
The issue was with the second argument I was passing into the sequence
method. That argument is static/evaluated at initialization. It is not dynamic, so timecop
did not have the chance to stub out the date before that second argument is evaluated.
trait(:sequenced_end_date) {sequence(:end_date, Date.today.year + 1) {|n| Date.new(n, 9, 30) }}
That Date.today.year
part of the second argument being passed into the sequence
method is static and evaluated at initialization. It is not dynamic and is already evaluated before the method is even called.
I simply needed to change those methods to the following:
trait(:sequenced_year_start_date) {sequence(:start_date) {|n| Date.new(Date.today.year, 10, 1) }}
trait(:sequenced_year_end_date) {sequence(:end_date) {|n| Date.new(Date.today.year + n, 9, 30) }}
Now it works as expected and returns the following:
Today's frozen date thanks to Timecop: 01/01/2010
Sequenced Date: 09/30/2011
Blocks are dynamic, so calling Date.today.year + n
within the block gives timecop the chance to first stub out Date
before this block is executed.