Rspec: how to test file operations and file conten

2020-02-17 04:48发布

In my app I have such code:

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

I want to test this code via rspec. What is the best practices for doing this?

5条回答
Anthone
2楼-- · 2020-02-17 05:21

For someone like me who need to modify multiple files in multiple directories (e.g. generator for Rails), I use temp folder.

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)
  end
end  
查看更多
虎瘦雄心在
3楼-- · 2020-02-17 05:24

This is how to mock File (with rspec 3.4), so you could write to a buffer and check its content later:

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
查看更多
Explosion°爆炸
4楼-- · 2020-02-17 05:39

I would suggest using StringIO for this and making sure your SUT accepts a stream to write to instead of a filename. That way, different files or outputs can be used (more reusable), including the string IO (good for testing)

So in your test code (assuming your SUT instance is sutObject and the serializer is named writeStuffTo:

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

String IO behaves like an open file. So if the code already can work with a File object, it will work with StringIO.

查看更多
smile是对你的礼貌
5楼-- · 2020-02-17 05:39

You can use fakefs.

It stubs filesystem and creates files in memory

You check with

File.exists? "filename" 

if file was created.

You can also just read it with

File.open 

and run expectation on its contents.

查看更多
看我几分像从前
6楼-- · 2020-02-17 05:43

For very simple i/o, you can just mock File. So, given:

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

then:

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

However, this approach falls flat in the presence of multiple reads/writes: simple refactorings which do not change the final state of the file can cause the test to break. In that case (and possibly in any case) you should prefer @Danny Staple's answer.

查看更多
登录 后发表回答