Hashrocket.com / blog

Bg default article large

Time-Sensitive Cucumber Testing

posted on and written by Paul Elliott in

Image 100x100 paul elliott

If you've written any cucumber features that rely on dates, you know what a pain it can be. What's worse, using Timecop or the like on the server has no impact on the browser and can cause inconsistencies in your client/server state. Luckily fabrication gives you some tools to alleviate that pain.

Fabrication ships with step definitions that let you create test objects with cucumber tables. A common practice is to use something like Timecop to travel to a point in time and run the tests as of that date. That would produce a scenario like this:

Given today is '2012-06-01'
And the following campaigns:
  | name        | starts at  | ends at    |
  | Current One | 2012-05-25 | 2012-06-03 |
  | Old One     | 2012-05-01 | 2012-05-10 |
  | Future One  | 2012-06-10 | 2012-06-15 |
When I am on the campaigns page
Then the "Current One" campaign should be active
And the "Old One" campaign should not be active
And the "Future One" campaign should not be active

Although this is a little tough to read, it will work fine as long as you don't have any javascript that compares the dates to the current time. If we leverage a Fabrication transform, however, then we can eliminate the time travel and make the test a little more readable.

Fabrication transforms act on any object fabricated through the official Fabrication steps. You can specify them on specific attributes of specific models and more than one can apply to a model, unlike the built-in cucumber table transforms.

In this case, let's parse the starts_at and ends_at dates with Chronic. We can do so by adding this to a steps file.

Fabrication::Transform.only_for(:post, :starts_at, lambda { |date|
  Chronic.parse(date)
}

Fabrication::Transform.only_for(:post, :ends_at, lambda { |date|
  Chronic.parse(date)
}

Now we can be a little less technical in our cucumber steps and use relative dates instead of these hard coded date strings.

Given the following campaigns:
  | name        | starts at        | ends at          |
  | Current One | 3 days ago       | 3 days from now  |
  | Old One     | 2 months ago     | 1 month ago      |
  | Future One  | 2 weeks from now | 1 month from now |
When I am on the campaigns page
Then the "Current One" campaign should be active
And the "Old One" campaign should not be active
And the "Future One" campaign should not be active

Now the browser and server can work as one without the time paradox!

Posted in Development and tagged with Ruby