RSpec: how to test file operations and file content
Solution 1
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.
Solution 2
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.
Solution 3
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
Solution 4
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.
Solution 5
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
Related videos on Youtube
petRUShka
Research fellow, mathematician, programmer. Professional code experience from 2005. I've started with coding assembler emulator in m4 for microprocessor development. Developer of several web-apps in Ruby (mostly in Ruby on Rails). Lead developer of internal bank web-based helpdesk for 10-30 thousand of bank employees. Developer of scientific distributed heterogeneous software system (GPGPU: OpenCL (PyOpenCL), Sage Math, Magma). Developer of several open source ruby gems. Developer of following VIM-plugins: vim-magma, vim-gap, vim-pycuda, vim-sage, vim-pyopencl.
Updated on September 11, 2020Comments
-
petRUShka over 3 years
In my app, I have the following code:
File.open "filename", "w" do |file| file.write("text") end
I want to test this code via RSpec. What are the best practices for doing this?
-
netbe almost 12 years@Wayne I'm wondering how you will proceed with testunit See [this question][1] [1]: stackoverflow.com/questions/11619884/…
-
-
Jazzepi over 10 yearsThis was an excellent answer. I wish I could upvote you more than once.
-
John Doe over 7 yearsGood answer, I know it was not asked but it would have been perfect if It included the partner 'read' example as well.
-
reducing activity over 7 yearsNote that FakeFS fails with Rspec (both Rspec 2 and Rspec 3) - github.com/fakefs/fakefs/issues/215
-
Marko Avlijaš over 7 yearsHow to modify the code to use String.IO? It seems resulting code will be much uglier, just so testing is easier?
-
Danny Staple over 7 yearsI've added more info to the answer on what the StringIO class does. You could combine my answer with Wayne's and mock('file') with a wrapper to a StringIO object.