TDD/BDD in Ruby - Chapter 4

To-Do List

$5 + 10 CHF = $10 if rate is 2:1
$5 * 2 - $10
Make "amount" private
Dollar side effects?
Money rounding?
equals()
hashCode()
Equal null
Equal object

Test Code


public void testMultiplication {
  Dollar five = new Dollar(5);
  assertEquals(new Dollar(10), five.times(2));
  assertEquals(new Dollar(15), five.times(3));
}

Test::Unit Code


require 'test/unit'
require 'dollar.rb'

class TestDollar < Test::Unit::TestCase
  def test_multiplication
    five = Dollar.new(5)
    assert_equal(five.times(2), Dollar.new(10))
    assert_equal(five.times(3), Dollar.new(15))
  end

  def test_equality
    assert_equal(Dollar.new(5), Dollar.new(5))
    assert_not_equal(Dollar.new(5), Dollar.new(6))
  end
end

RSpec Code


require 'spec'
require 'dollar.rb'

class DollarSpecification < Spec::Context
  def multiplication
    five = Dollar.new(5)
    five.times(2).should.equal Dollar.new(10)
    five.times(3).should.equal Dollar.new(15)
  end

  def equality
    Dollar.new(5).should.equal Dollar.new(5)
    Dollar.new(5).should.not.equal Dollar.new(6)
  end
end

Dollar Class Code


class Dollar
  attr_reader :amount

  def initialize(amount)
    @amount = amount
  end

  def times(multiplier)
    Dollar.new(@amount * multiplier)
  end

  def eql?(object)
    self == (object)
  end

  def ==(object)
    object.equal?(self) ||
      (object.instance_of?(self.class) && object.amount == @amount)
  end
end

The interesting part is how to handle equality properly. In the previous chapter, I mistakenly thought equals() was an arbitrary method that Kent Beck came up with, but in fact it's part of the Java Core API. The equivalent in Ruby is (). However, when I overloaded (), it was apparent that a simple object.amount == @amount was not the proper way to compare objects. After searching and asking questions in #ruby-lang, I found the "right way" to implment ==()/equals() on Simon Harris' blog entry on How To Write eql?() in Ruby.