Home > Articles > Programming > Ruby

  • Print
  • + Share This
From the author of

Always Look for New or Changed Requirements

Whenever you write unit tests, think about what the requirements really mean. Remember to ask for clarification from your users and testers if something is unclear. Sometimes just asking questions will enable you to see a much simpler, more flexible design choice.

How club members are supposed to be selected is unclear from the use case. Rather than burden the Member class with different matching algorithms, I chose a design that passes a code block into the matches? method so that whoever calls the method can decide what a "match" is. In the test code below, two different blocks are used: one that matches on name, the other on email address.

require 'test/unit'
require 'Member'

class TC_Member < Test::Unit::TestCase
 def set_up
  @runner1 = Member.new("Pete", "pete@mcbreen.ab.ca", "")
  @runner2 = Member.new("Peter", "", "")
 end
 def test_name
  assert_equal("Pete", @runner1.name, "Mismatch")
  assert_equal("Peter", @runner2.name, "Mismatch")
 end
 def test_matches
  runner = @runner1.matches?("Pete") {|r,n|
   if n == r.name then r else nil end}
  assert_equal(@runner1, runner, "Mismatch")
  runner = @runner2.matches?("") {|r,n|
   if n == r.email then r else nil end}
  assert_equal(@runner2, runner, "Mismatch")
 end
end

Once this design is chosen, the implementation of the Member class is simple, but to understand it you will probably have to RTFM to understand what yield is doing.

class Member
 attr_accessor :name, :email, :phone
 def initialize (runner_name, email_address, phone_number)
  @name = runner_name
  @email = email_address
  @phone = phone_number
 end
 def matches? (str, &comparison)
  yield (self, str) #invoke the comparison block
 end
end

This design choice moves the complexity out of the Member class and into whatever wants to do the selection, but in the process makes the design a lot more flexible. Queries can now be designed to pick up runners who live in different locations or prefer to race different distances, merely by passing in a different comparison script (provided that we add the appropriate attributes to the Member class).

  • + Share This
  • 🔖 Save To Your Account