Test-Driven Rails Helper

Dan Croak

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'

About thoughtbot

We've been helping engineering teams deliver exceptional products for over 20 years. Our designers, developers, and product managers work closely with teams to solve your toughest software challenges through collaborative design and development. Learn more about us.