2014-09-01 42 views
0

我正在寫一個測試我的課具有下面的構造之一:如何測試讀取文件?

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和增量在每一行出於某種原因,在測試過程中不會發生。我怎樣才能確保它呢?

+1

順便說一句,'open'和'each'鏈接與將'block'傳遞給'open'不同 - 你的示例中的文件對象將不會自動關閉。 – Stefan 2014-09-01 14:38:25

回答

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

你可以(也應該)一起使用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 
0

如果你不想惹嘲笑File每個測試你可以嘗試FakeFS WH各地它實現了一個基於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如何實現它們。