views:

1700

answers:

3

I want to use RSpec mocks to provide canned input to a block.

Ruby:

class Parser
  attr_accessor :extracted

  def parse(fname)
    File.open(fname).each do |line|
      extracted = line if line =~ /^RCS file: (.*),v$/
    end
  end
end

RSpec:

describe Parser
  before do
    @parser = Parser.new
    @lines = mock("lines")
    @lines.stub!(:each)
    File.stub!(:open).and_return(@lines)
  end

  it "should extract a filename into extracted" do
    linetext = [ "RCS file: hello,v\n", "bla bla bla\n" ]

    # HELP ME HERE ...
    # the :each should be fed with 'linetext'
    @lines.should_receive(:each)

    @parser.should_receive('extracted=')
    @parser.parse("somefile.txt")
  end
end

It's a way to test that the internals of the block work correctly by passing fixtured data into it. But I can't figure out how to do the actual feeding with RSpec mocking mechanism.

update: looks like the problem was not with the linetext, but with the:

@parser.should_receive('extracted=')

it's not the way it's called, replacing it in the ruby code with self.extracted= helps a bit, but feels wrong somehow.

+1  A: 

I don't have a computer with Ruby & RSpec available to check this, but I suspect you need to add a call to and_yields call [1] on the end of the should_receive(:each). However, you might find it simpler not to use mocks in this case e.g. you could return a StringIO instance containing linetext from the File.open stub.

[1] http://rspec.rubyforge.org/rspec/1.1.11/classes/Spec/Mocks/BaseExpectation.src/M000104.html

floehopper
and_yields does not do what I need, and if it does - then I just cant wrap my head around how the hell it works.
Evgeny
No, you are right. It looks like the bug I have is actually in [email protected]_receive('extracted='). it's just not correct ... does not work.
Evgeny
When I replaced "extracted = " with "self.extracted = " in the ruby code, it started working correctly. Was chasing the wrong bug for the last two days.
Evgeny
+1  A: 

I would go with the idea of stubbing the File.open call

lines = "RCS file: hello,v\n", "bla bla bla\n"
File.stub!(:open).and_return(lines)

This should be good enough to test the code inside the loop.

Jeff Waltzer
+1  A: 

This should do the trick:

describe Parser
  before do
    @parser = Parser.new
  end

  it "should extract a filename into extracted" do
    linetext = [ "RCS file: hello,v\n", "bla bla bla\n" ]
    File.should_receive(:open).with("somefile.txt").and_return(linetext)
    @parser.parse("somefile.txt")
    @parser.extracted.should == "hello"
  end
end

There are some bugs in the Parser class (it won't pass the test), but that's how I'd write the test.

Titanous