---
title: Test-Driven Rails Helper
teaser:
tags: web,testing,rails
author: Dan Croak
published_on: 2009-10-15
---

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:

```ruby
# 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](https://thoughtbot.com/blog/post/392707640/custom-date-and-time-formats-in-rails):

    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 <abbr
title="HyperText Markup Language">HTML</abbr>, the extra space is fine.

Make it pass:

    month_day: ! '%B %e'
