web-dev-qa-db-fra.com

Rspec: comment tester les opérations sur les fichiers et leur contenu

Dans mon application, j'ai un tel code:

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

Je veux tester ce code via rspec. Quelles sont les meilleures pratiques pour le faire?

39
petRUShka

Je suggère d'utiliser StringIO pour cela et de vous assurer que votre SUT accepte un flux dans lequel écrire au lieu d'un nom de fichier. De cette façon, différents fichiers ou sorties peuvent être utilisés (plus réutilisables), y compris la chaîne IO (utile pour les tests).

Donc, dans votre code de test (en supposant que votre instance SUT est sutObject et que le sérialiseur s'appelle writeStuffTo:

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

La chaîne IO se comporte comme un fichier ouvert. Donc, si le code peut déjà fonctionner avec un objet File, il fonctionnera avec StringIO.

55
Danny Staple

Pour des entrées/sorties très simples, vous pouvez simplement vous moquer de File. Donc, étant donné:

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

puis:

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

Cependant, cette approche échoue en présence de plusieurs lectures/écritures: de simples refactorisations qui ne modifient pas l'état final du fichier peuvent provoquer la rupture du test. Dans ce cas (et éventuellement dans tous les cas), vous devriez préférer la réponse de @Danny Staple.

46
Wayne Conrad

Voici comment simuler un fichier (avec rspec 3.4), afin que vous puissiez écrire dans un tampon et vérifier son contenu ultérieurement:

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
18
Eduardo Santana

Vous pouvez utiliser fakefs

Il arrête le système de fichiers et crée des fichiers en mémoire

Vous vérifiez avec

File.exists? "filename" 

si le fichier a été créé.

Vous pouvez aussi simplement le lire avec 

File.open 

et lancez des attentes sur son contenu. 

17
mehowte

Pour quelqu'un comme moi qui doit modifier plusieurs fichiers dans plusieurs répertoires (par exemple, un générateur pour Rails), j'utilise un dossier temporaire.

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)
0
lulalala