如何测试读取文件?

时间:2014-09-01 14:07:30

标签: ruby rspec

我正在为我的一个类编写测试,其中包含以下构造函数:

def initialize(filepath)
    @transactions = [] 
    File.open(filepath).each do |line|
      next if $. == 1
      elements = line.split(/\t/).map { |e| e.strip }
      transaction = Transaction.new(elements[0], Integer(1))
      @transactions << transaction 
    end
end

我想通过使用假文件而不是夹具来测试它。所以我写了以下规范:

it "should read a file and create transactions" do      
    filepath = "path/to/file"
    mock_file = double(File)

    expect(File).to receive(:open).with(filepath).and_return(mock_file) 
    expect(mock_file).to receive(:each).with(no_args()).and_yield("phrase\tvalue\n").and_yield("yo\t2\n")

    filereader = FileReader.new(filepath)
    filereader.transactions.should_not be_nil
end

不幸的是,这失败了,因为我依靠$.等于1并在每一行上递增,并且由于某些原因在测试期间没有发生。我怎样才能确保它呢?

3 个答案:

答案 0 :(得分:2)

全局变量使代码难以测试。您可以使用each_with_index

File.open(filepath) do |file|
  file.each_with_index do |line, index|
    next if index == 0 # zero based
    # ...
  end
end

但看起来您正在使用标题行解析CSV文件。因此,我使用Ruby CSV library

require 'csv'

CSV.foreach(filepath, col_sep: "\t", headers: true, converters: :numeric) do |row|
  @transactions << Transaction.new(row['phrase'], row['value'])
end

答案 1 :(得分:1)

您可以(并且应该)将IO#each_lineEnumerable#each_with_index一起使用,如下所示:

File.open(filepath).each_line.each_with_index do |line, i|
  next if i == 1
  # …
end

或者您可以删除第一行,并与其他人合作:

File.open(filepath).each_line.drop(1).each do |line|
  # …
end

答案 2 :(得分:0)

如果你不想为每个测试捣乱File,你可以尝试FakeFS实现一个基于StringIO的内存文件系统,它会在测试后自动清理。

这样,如果您的实施发生变化,您的测试就不需要改变。

require 'fakefs/spec_helpers'

describe "FileReader" do
  include FakeFS::SpecHelpers

  def stub_file file, content
    FileUtils.mkdir_p File.dirname(file)
    File.open( file, 'w' ){|f| f.write( content ); }
  end

  it "should read a file and create transactions" do      
    file_path    = "path/to/file"
    stub_file file_path, "phrase\tvalue\nyo\t2\n"

    filereader = FileReader.new(file_path)
    expect( filereader.transactions ).to_not be_nil
  end
end

警告:这是Ruby中大多数文件访问的实现,尽可能将其传回原始方法。如果你正在做任何高级文件,你可能会开始遇到FakeFS实现中的错误。我遇到了一些二进制文件字节读/写操作,这些操作在FakeFS中没有实现Ruby的实现方式。