19 January 2012

Storing values outside your stub with Rspec

I'm working on some code today that saves records to a database. For my rspec tests I want to redirect the "save" to a stub. And I want to take the data that would have been saved and stow it somewhere else. Well, I had plenty of problems with this so I'm here to share the real solution.

The first issue that I ran into: return values on stubs must match the type of the return value in your live code. Duh. But I'm coming from the static typed world, so this one slipped by me. I had a method that was returning the number of new records created (0 or 1). Simple. But the stub was just pushing the input into an array:

  saved_rows = []
    proc.stub(:save) do |arg0| 
      puts 'redirecting save'
      saved_rows << arg0
    end


Which meant that the return value (that was going to 'real' code) was a big array. No bueno. Ok, fixing that was a huge aha moment. Just add a '0' on the line after  @saved_rows << arg0. Much cursing about this one -- the object worked find from irb but failed when I did the test. 


Next up: storing the row in an array outside of the stub. I couldn't find any samples of this, so it was trial and error. In the code above, it certainly looks like it'd work. But it doesn't. I assume saved_rows is getting created in two places. The solution: use an @instance variable: 


    @saved_rows = []
    proc.stub(: save ) do |arg0|
      puts " redirecting save "
      @saved_rows << arg0
      0
    end


Bada-bing. Now I have a save method that is storing the new data in a place where I can get to it (and test it). 

Related Rspec doc:

https://www.relishapp.com/rspec/rspec-mocks/docs/method-stubs/stub-with-substitute-implementation