This is how I unit tested a view helper to format a date range in a view.
I wrote the interface for the view helper I wanted, but which did not exist yet:
= format_date_range(course.date_range)
The outer integration tests (not shown) failed with NoMethodError:
format_date_range
.
I generated a new helper file:
script/generate helper date_time
It produced:
# app/helpers/date_time_helper.rb
module DateTimeHelper
end
# test/unit/helpers/date_time_helper_test.rb
require 'test_helper'
class DateTimeHelperTest < ActionView::TestCase
end
I wrote the unit test:
should 'format date range on same day' do
eight_oclock = DateTime.new(2009, 10, 12, 8)
nine_oclock = DateTime.new(2009, 10, 12, 9)
date_range = eight_oclock..nine_oclock
expected = 'October 12, 2009'
assert_equal expected, format_date_range(date_range)
end
This style is flat test structure, intention-revealing temporary variables, respect an 80-character line limit.
I made it pass:
def format_date_range(date_range)
first = date_range.first
last = date_range.last
if same_day?(first, last)
"#{first.to_s(:month_day)}, #{first.year}"
end
end
private
def same_day?(date_one, date_two)
date_one.day == date_two.day
end
Like the test, I used intention-revealing temporary variables and a private method for a more expressive question in the conditional.
I wrote another test case:
should 'format date range on different days of same month' do
monday = DateTime.new(2009, 10, 12)
tuesday = DateTime.new(2009, 10, 13)
date_range = monday..tuesday
expected = 'October 12-13, 2009'
assert_equal expected, format_date_range(date_range)
end
I made it pass:
def format_date_range(date_range)
# ...
elsif same_month?(first, last)
"#{first.to_s(:month_day)}-#{last.day}, #{last.year}"
end
end
private
def same_month?(date_one, date_two)
date_one.month == date_two.month
end
Rails date and time formatting is done in locales:
en:
date:
formats:
long: ! '%B %d, %Y'
month_day: ! '%B %d'
short: ! '%b %d'
I wrote another test case:
should 'format date range on days of different months' do
october = DateTime.new(2009, 10, 31)
november = DateTime.new(2009, 11, 1)
date_range = october..november
expected = 'October 31-November 01, 2009'
assert_equal expected, format_date_range(date_range)
end
I made it pass:
else
"#{first.to_s(:month_day)}-#{last.to_s(:month_day)}, #{last.year}"
end
I wanted to remove the leading zero sucks. We could use %e
instead of
%d
, which replaces the leading zero with a space:
expected = "October 31-November 1, 2009"
That makes the test look a little ugly but since the output will be HTML, the extra space is fine.
Make it pass:
month_day: ! '%B %e'