RSpec mocks: every block

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

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 

This is a way to verify the correct operation of indoor units by transferring embedded data into it. But I can’t understand how to make the actual power using the mechanical mechanism of RSpec.

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

 @parser.should_receive('extracted=') 

this is not what he called, replacing it in ruby ​​code with self.extracted = helps a bit, but it feels somehow wrong.

+6
ruby mocking rspec rspec-mocks
source share
4 answers

I don’t have a computer with Ruby and RSpec to test this, but I suspect you need to add a call and_yields call [1] at the end of should_receive(:each) . However, you may find that it is easier not to use mocks in this case, for example. you can 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

+2
source share

To understand how and_yield works: I don’t think that and_return is really what you want here. This will set the return value of the File.open block, not the rows received for its block. To change the example a bit, say you have this:

Ruby

 def parse(fname) lines = [] File.open(fname){ |line| lines << line*2 } end 

Rspec

 describe Parser do it 'should yield each line' do File.stub(:open).and_yield('first').and_yield('second') parse('nofile.txt').should eq(['firstfirst','secondsecond']) end end 

Will pass. If you replaced this line with "and_return", like

 File.stub(:open).and_return(['first','second']) 

It will crash because the block bypasses:

 expected: ["firstfirst", "secondsecond"] got: ["first", "second"] 

So, the bottom line uses "and_yield" to mock the input to each type of block. Use 'and_return' to make fun of the output of these blocks.

+7
source share

I would go with the idea of ​​caving 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 check the code inside the loop.

+2
source share

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 errors in the Parser class (it will not pass the test), but I have to write a test.

+1
source share

All Articles