8

我想使用 RSpec 模拟为块提供固定输入。

红宝石:

class Parser
  attr_accessor :extracted

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

规格:

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

这是一种通过将固定数据传递给块来测试块内部是否正常工作的方法。但我不知道如何使用 RSpec 模拟机制进行实际的馈送。

更新:看起来问题不在于 linetext,而在于:

@parser.should_receive('extracted=')

这不是它的调用方式,在 ruby​​ 代码中用 self.extracted= 替换它有点帮助,但不知何故感觉不对。

4

4 回答 4

9

充实“and_yield”的工作原理:我认为“and_return”并不是你真正想要的。这将设置 File.open 块的返回值,而不是为其块产生的行。稍微改变一下这个例子,假设你有这个:

红宝石

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

规格

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

将通过。如果您将该行替换为“and_return”,例如

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

它将失败,因为该块被绕过:

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

所以底线是使用'and_yield'来模拟'each'类型块的输入。使用“and_return”模拟这些块的输出。

于 2012-07-03T14:11:23.237 回答
4

我没有一台装有 Ruby 和 RSpec 的计算机来检查这个,但我怀疑你需要and_yieldsshould_receive(:each). 但是,您可能会发现在这种情况下不使用模拟更简单,例如您可以从存根返回一个StringIO包含的实例。linetextFile.open

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

于 2008-12-18T17:40:27.970 回答
2

我会采用存根 File.open 调用的想法

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

这应该足以测试循环内的代码。

于 2008-12-18T22:35:05.457 回答
1

这应该可以解决问题:

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

Parser 类中有一些错误(它不会通过测试),但这就是我编写测试的方式。

于 2008-12-20T17:43:51.827 回答