How to time travel safely, but only on your tests!
During your journey as developer you will need to manipulate time, and by this I mean freezing it, warping to the future or back to the past. We use this when we want to test some code behavior determined by some date. For example, we may need to test some status change of an object when the date is a past one.
As always, there are some gems that help our lives. I believe the most used one is the gem Timecop. In the past Delorean was used as well, but for now I would say it looks a little bit outdated.
Besides those facts, it is valid to argue that the inclusion of another dependency in the system, for the sake of handling a specific function on our tests, may be a bit overkill. Because of this, in the Rails 4.1 update a new helper was added to rescue us: TimeHelpers.
Ok, I know. This version is old. The fact here is that a lot of people have no knowledge that this helper exists. Sometimes it may be necessary to keep the Timecop inside the system, mostly because it has some other functions comparing to the helper, but on my experience, the helper is enough in the majority of cases.
How do I even start?
First, you must be using Rails 4.1 version or higher. Now you must decide if you want to turn this helper available for all the tests or not. If you want to use it only on one test (and this is totally fine), you can do something like this :
But if you think it is a better idea to let this helper available for all your tests, all you have to do is add one line of code to your test configuration. If you are using Rspec it would be something like this:
With this you will have 3 new methods to use:
travel_back. The first 2 are almost the same thing as
they are used to lock the time. When you use
travel you will need to pass a specific time to be added on top of the current time.
travel_to the time you passes will become the current time and will stay locked on it.
As you can see both methods act almost the same: stubbing the return of some methods in order to lock the time.
Those methods are:
The last method of the helper,
travel_back, is the one responsible to remove all the stubs made before. This way
Time.now will return the real current time.
travel_to accept a block as well:
If using a block the time will revert back when the block execution is over. If you are using those methods without a block,
depending on your tests, you may need to call
travel_back to normalize the situation.
As stated at the top of this post, there are some differences if we compare the helper and the gem Timecop. The one you must have special attention is the difference when we return time back to normal.
If you are using the helpers in sequence, for example:
What will happen here is that when you call
travel_back the time will return to the real current time, ignoring any other stub you could have made,
even if they were inside a block. We can understand it better if we check the code for the method here:
stubs generated are removed! This will not happen on Timecop for example. The method that does the same on the gem is the
You can check the code for the whole class here.
Having a look at the method:
It is possible to notice that it has 2 different types of action here, where the first part will handle the situation where you have stubbed a previous
Time, enforcing that you will come back to this
stub instead of the real current time. This will determinate if you need to use Timecop or not
on your tests.
If you are cleaning your system or you cannot stand adding unnecessary dependencies to your code (me \o), here is a good example of native Rails feature you can start using today. Just be aware of the differences. I recommend that you take a look at the GitHub page of Timecop and compare it to the helper sou you can be safe and avoid future problems.