2010-11-01 1 views
39

Nella mia app ho tale codice:Rspec: come testare le operazioni di file e file contenuti

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

Voglio testare questo codice tramite RSpec. Quali sono le migliori pratiche per fare questo?

+0

@Wayne mi chiedo come si intende procedere con TestUnit Sede [questa domanda] [1] [1]: http://stackoverflow.com/questions/11619884/ testunit-how-to-test-file-operations-and-file-content – netbe

risposta

53

Suggerirei di utilizzare StringIO per questo e accertarmi che il SUT accetti uno stream per scrivere anziché un nome file. In questo modo, i file o le diverse uscite può essere utilizzato (più riutilizzabile), tra cui la stringa IO (buono per la prova)

Quindi nel tuo codice di prova (supponendo l'istanza SUT è sutObject e il serializzatore si chiama writeStuffTo:

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

String IO si comporta come un file aperto. Quindi, se il codice già in grado di lavorare con un oggetto File, che possa funzionare con StringIO.

+0

Questa è stata una risposta eccellente. Vorrei poterti invogliare più di una volta. – Jazzepi

+1

Buona risposta, so che non è stato chiesto ma sarebbe stato perfetto se includesse anche l'esempio di 'lettura' del partner. –

+1

Come modificare il codice per utilizzare String.IO? Sembra che il codice risultante sarà molto più brutto, solo così il test è più facile? –

44

Per molto semplice I/O, è possibile presentare solo finta. Così, dato :

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

poi:

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 

Tuttavia, questo approccio cade piatto in presenza di molteplici letture/scritture: semplici refactoring che non cambiano lo stato finale del file può causare il test di rottura. In tal caso (e possibilmente in ogni caso) dovresti preferire la risposta di @Danny Staple.

17

È possibile utilizzare fakefs.

E stub filesystem e crea file nella memoria

di verificare con

File.exists? "filename" 

se il file è stato creato.

Si può anche semplicemente leggere con

File.open 

ed eseguire le aspettative sul suo contenuto.

+1

Nota che FakeFS fallisce con Rspec (sia Rspec 2 che Rspec 3) - https://github.com/fakefs/fakefs/issues/215 –

10

Questo è il modo per deridere file (con RSpec 3.4), così si potrebbe scrivere in un buffer e controllarne il contenuto di seguito:

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 
0

Per uno come me che hanno bisogno di modificare più file in più directory (es. generatore per Rails), utilizzo la cartella temporanea.

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)