Dates and Times
Learn how to test dates and times efficiently.
Date and time logic has a well-deserved reputation as some of the most complex and irritating logic in any application. Testing calendar logic, including time-based reports, automatic logouts, and “1 day ago” text displays, can be a particular headache. Still, we can do a couple of things to simplify the time-logic beast.
Part of the problem
We have a test that uses factories. We’d like to test some time-based code that might be used in a search or report result. This goes in spec/unit/project_spec.rb
:
let(:runway) { create(:project, name: "Project Runway",start_date: "2018-01-20" }let(:greenlight) { create(:project, name: "Project Greenlight",start_date: "2018-02-24" }let(:gutenberg) { create(:project, name: "Project Gutenberg",start_date: "2018-01-31" }it "finds recently started projects" doactual = Project.find_recently_started(6.months)expect(actual.size).to eq(3)end
Here’s code that makes the test pass, from app/models/project.rb
:
def self.find_recently_started(time_span)old_time = Date.today - time_spanall(conditions: ["start_date > ?", old_time.to_s(:db)])end
On January 20, 2018, the test passed. And on January 21, it will pass, and it will pass days after that as well.
Six months later, about July 20 (when we’ve probably long forgotten about this test, this sample data, and maybe even this entire project), the test will fail. And we’ll spend way too much time trying to figure out what happened until we remember the date issue and realize that the project with a January 20 date is no longer within the six-month time span specified in the test. Of course, changing all the dates pushes the problem forward and gives us time to forget all about it again.
This issue may sound silly, but like many of the more ridiculous examples in the course, this happened and can end up costing a lot of time. We must have seen tests that fail at a particular time of day (because time zones used inconsistently push part of the code into the next day). We must have seen tests that pass in Chicago but fail in California. Likewise, we must have seen tests that fail on the first day of a new month and a new year, and of course, tests that fail on the boundary in and out of daylight savings time. Most of this can be prevented.
Solution to the problem
We can solve this problem by adding an optional argument to every ...