Travel through Time within your Cukes with ActiveSupport::Testing::TimeHelpers
One trait of a good test is to be invariant over time. This basically means that if it passes once, there is no reason that it fails another time we run it. One thing among many others that can play this trick on you is the time, for instance if you rely on Date.today or DateTime.now. Take for instance an application in which you want your users to create ads with specific expiration dates, but prevent them from setting these up in the past.
You would come up with a feature containing the following scenarios:
Background: Given I am logged in And today is '03-20-2016' And I am on the ad creation page Scenario: using a valid date When I submit the form with: | title | date | | My title | 04-20-2016 | Then I should be at 'My title' description page Scenario: using a date in the past When I submit the form with: | title | date | | My title | 02-20-2016 | Then I should see "Date cannot be in the past!"
Fortunately for us and thanks to the travel_to method from ActiveSupport::Testing::TimeHelpers introduced in Rails 4.1, the current date can be faked without too much overhead (any C# developers? :D). In order to travel trough time within our Scenarios, we can then provide the following step:
Given(/^today is "(.*?)"$/) do |date_string| travel_to Date.parse(date_string) end
Don’t forget to travel back to the present though, to prevent your scenarios from leaking. We can do this using the travel_back method within an After hook to make sure that we’re in a clean state after each scenario:
After do travel_back end
If you always need to use the same date as reference, you can also work with a tag. Using a tag has the advantage to fake the Date only when you really need it:
@today_is_04-20-2016 Scenario: using a date in the past When I submit the form with: | title | date | | My title | 02-20-2016 | Then I should see "Date cannot be in the past!"
Before('@today_is_04-20-2016') do travel_to Date.parse('04-20-2016') end After('@today_is_04-20-2016') do travel_back end
Note: With the latest release of Cucumber by the time I’m writing this post, I wasn’t able to call travel_to in an Around hook, before calling the scenario block, which would have been quite handy.
You might prefer the first solution because it’s easier to read or because it underlines clearly the fact that your user is at a specific point in time. Up to you.
Finally, you can safely write the rest of your application, including your model relying on the current date:
class Ad < ActiveRecord::Base validate :expiration_date_not_in_the_past def expiration_date_not_in_the_past errors.add(:expiration_date, " cannot be in the past!") if expiration_date < Date.today end endCucumber, Rails, RSpec. Bookmark the permalink.