Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing memoization

I have an expensive method called calculate_total. I need a method called total that will return the result of calculate_total. Subsequent calls to total should return the previous result of calculate_total.

I want to do this in a test driven way. Here are my tests (I'm using RSpec):

describe Item do
  describe "total" do
    before do
      @item = Item.new
      @item.stub!(:calculate_total => 123)
    end

    it "returns the calculated total" do
      @item.total.should == 123
    end

    it "subsequent calls return the original result" do
      previous_total = @item.total
      @item.total.should equal(previous_total)
    end
  end
end

This is not a good test because the following method makes the tests pass, but I was expecting the second test to fail:

def total
  calculate_total
end

The reason is calculate_total returns a Fixnum so ruby doesn't see the result as 2 different objects. I was expecting the second test to fail, so then I could do the following to make it pass:

def total
  @total ||= calculate_total
end

Anyone know a better way to test this?

I don't think this is the best/correct way to test it, but I've settled on this: https://gist.github.com/1207270

like image 488
Austin Avatar asked Sep 09 '11 20:09

Austin


1 Answers

I think your gist is fine: what you want to test is whether or not calculate_total is called more than once, and that's exactly what you're doing. The only difference I might suggest is a slightly more explicit test:

it "subsequent calls don't calculate the total, but still return the original result" do
  @item.should_receive(:calculate_total).once
  2.times do 
    @item.total.should == 123
  end
end
like image 98
Mando Escamilla Avatar answered Sep 28 '22 21:09

Mando Escamilla