- Introduction
- Starting Test-Driven Development
- Always Look for New or Changed Requirements
- Sending Email Using Ruby
- Refactor Your Code To Make It Maintainable
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 endOnce 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).