RSpec模拟一个 :each 块

8
我想使用RSpec模拟来为代码块提供预先设定的输入。
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

这是一种通过向块传递固定数据来测试其内部是否正常工作的方法。但我不知道如何使用 RSpec 的模拟机制进行实际的数据输入。
更新:看起来问题并不在于 linetext,而是在于:
@parser.should_receive('extracted=')

这句话的意思是:“这不是被称为的方式,用self.extracted=来替换Ruby代码中的它会有所帮助,但感觉有些不对。”

一个 bug 被提取出来应该是实例变量 @extracted。请查看我的评论以获取正确的测试。 - titanous
4个回答

9
为了解释'and_yield'的工作原理:我认为在这里使用'and_return'并不是你真正想要的。它将设置File.open块的返回值,而不是传递给块的行。稍微修改一下这个例子,比如说你有这样一个代码段:
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

会通过。如果你将那行代码替换为'and_return',例如:
File.stub(:open).and_return(['first','second'])

由于块被绕过,它将失败:

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

所以,最重要的是使用“and_yield”来模拟输入到“each”类型块中。使用“and_return”来模拟这些块的输出。

4

and_yields并不能满足我的需求,即使它能够胜任,我也无法理解它是如何工作的。 - Evgeny
不,你是对的。看起来我遇到的错误实际上在@parser.should_receive('extracted=')中。它只是不正确...无法工作。 - Evgeny
当我在 Ruby 代码中将 "extracted =" 替换为 "self.extracted =" 时,它开始正常工作。在过去的两天里一直在追踪错误的 bug。 - Evgeny

2

我会采用桩方法来模拟File.open调用

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

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


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

解析器类中有一些错误(它无法通过测试),但这就是我编写测试的方式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接