Rspec: как проверить файловые операции и содержимое файла

В моем приложении у меня есть такой код:

File.open "filename", "w" do |file|
  file.write("text")
end

Я хочу проверить этот код через rspec. Какова лучшая практика для этого?

5 ответов

Решение

Я бы предложил использовать StringIO для этого и убедитесь, что ваше SUT принимает поток для записи вместо имени файла. Таким образом, можно использовать разные файлы или выходные данные (более пригодные для повторного использования), включая строку IO (подходит для тестирования)

Так в вашем тестовом коде (при условии, что ваш экземпляр SUT sutObject и сериализатор назван writeStuffTo:

testIO = StringIO.new
sutObject.writeStuffTo testIO 
testIO.string.should == "Hello, world!"

Строка ввода-вывода ведет себя как открытый файл. Так что, если код уже может работать с объектом File, он будет работать с StringIO.

Для очень простого ввода-вывода вы можете просто смоделировать File. Итак, учитывая:

def foo
  File.open "filename", "w" do |file|
    file.write("text")
  end
end

затем:

describe "foo" do

  it "should create 'filename' and put 'text' in it" do
    file = mock('file')
    File.should_receive(:open).with("filename", "w").and_yield(file)
    file.should_receive(:write).with("text")
    foo
  end

end

Однако этот подход не работает при наличии нескольких операций чтения / записи: простой рефакторинг, который не изменяет конечное состояние файла, может привести к сбою теста. В этом случае (и, возможно, в любом случае) вы должны предпочесть ответ @Danny Staple.

Вот как смоделировать File (с помощью rspec 3.4), чтобы вы могли записать в буфер и проверить его содержимое позже:

it 'How to mock File.open for write with rspec 3.4' do
  @buffer = StringIO.new()
  @filename = "somefile.txt"
  @content = "the content fo the file"
  allow(File).to receive(:open).with(@filename,'w').and_yield( @buffer )

  # call the function that writes to the file
  File.open(@filename, 'w') {|f| f.write(@content)}

  # reading the buffer and checking its content.
  expect(@buffer.string).to eq(@content)
end

Вы можете использовать fakefs.

Это заглушает файловую систему и создает файлы в памяти

Вы проверяете с

File.exists? "filename" 

если файл был создан.

Вы также можете просто прочитать это с

File.open 

и запустить ожидание на его содержание.

Для кого-то вроде меня, которому нужно изменить несколько файлов в нескольких каталогах (например, генератор для Rails), я использую временную папку.

Dir.mktmpdir do |dir|
  Dir.chdir(dir) do
    # Generate a clean Rails folder
    Rails::Generators::AppGenerator.start ['foo', '--skip-bundle']
    File.open(File.join(dir, 'foo.txt'), 'w') {|f| f.write("write your stuff here") }
    expect(File.exist?(File.join(dir, 'foo.txt'))).to eq(true)
Другие вопросы по тегам