A Neat Little Rails Testing Pattern

Inside my tests I try and keep everything I need inside the test, rather than depending on too many fixtures. That way I ensure they’re as readable as possible, and more importantly, obvious.

But, take the following example:

def test_total_is_due_now_if_return_is_less_than_14_days  booking = Booking.new(    :customer => customers(:valerie),    :collect_at => 1.day.from_now.to_date,    :return_at => 13.days.from_now.to_date,    :boxes => 2,    :collection_address => addresses(:one))  assert_equal Date.today.to_s, booking.final_payment_due_date.to_senddef test_payment_due_14_days_from_now  booking = Booking.new(    :customer => customers(:valerie),    :collect_at => 1.day.from_now.to_date,    :return_at => 20.days.from_now.to_date,    :boxes => 2,    :collection_address => addresses(:one))
  assert_equal 6.days.from_now.to_date.to_s, booking.final_payment_due_date.to_send

Well, everything’s in the test, but there’s a fair bit of duplication. What would be nicer, is to take the prototype booking above and just override certain values.

Instead we might write something like

def test_total_is_due_now_if_return_is_less_than_14_days  booking = new_booking_with(:return_at => 13.days.from_now.to_date)  assert_equal Date.today.to_s, booking.final_payment_due_date.to_senddef test_payment_due_14_days_from_now  booking = new_booking_with(:return_at => 20.days.from_now.to_date)  assert_equal 6.days.from_now.to_date.to_s, booking.final_payment_due_date.to_send
privatedef new_booking_with(args)  Booking.new(args.reverse_merge({    :customer => customers(:valerie),    :collect_at => 1.day.from_now.to_date,    :return_at => 15.days.from_now.to_date,    :boxes => 2,    :collection_address => addresses(:one)  }))end

Much nicer (to me anyway), particularly once you start adding more and more tests (and especially validation related ones).