2010-06-04 7 views
3

Il ya quelque temps, j'ai demandé "How to test obtaining a list of files within a directory using RSpec?" et même si j'ai eu quelques réponses utiles, je suis toujours coincé, d'où une nouvelle question avec plus de détails sur ce que je suis en train de faire.Comment copier/coller un répertoire de fichiers et leur contenu en utilisant RSpec?

J'écris mon premier RubyGem. Il a un module qui contient une méthode de classe qui retourne un tableau contenant une liste de fichiers non-cachés dans un répertoire spécifié. Comme ceci:

files = Foo.bar :directory => './public' 

Le tableau contient également un élément qui représente les métadonnées sur les fichiers. C'est en fait un hachage de hachages générés à partir du contenu des fichiers, l'idée étant que changer même un seul fichier change le hachage.

J'ai écrit mes exemples RSpec en attente, mais je ne sais vraiment pas comment les mettre en œuvre:

it "should compute a hash of the files within the specified directory" 
it "shouldn't include hidden files or directories within the specified directory" 
it "should compute a different hash if the content of a file changes" 

Je ne veux pas avoir des tests qui dépendent des fichiers réels agissant en tant que luminaires. Comment puis-je mocker ou écraser les fichiers et leur contenu? L'implémentation de gem utilisera Find.find, mais comme l'une des réponses à mon autre question dit, je n'ai pas besoin de tester la bibliothèque.

Je n'ai vraiment aucune idée de comment écrire ces spécifications, donc toute aide très appréciée!


Modifier: La méthode cache est la méthode ci-dessous, je suis en train de tester:

require 'digest/md5' 
require 'find' 

module Manifesto 
    def self.cache(options = {}) 
    directory = options.fetch(:directory, './public') 
    compute_hash = options.fetch(:compute_hash, true) 
    manifest = [] 
    hashes = '' 
    Find.find(directory) do |path| 

     # Only include real files (i.e. not directories, symlinks etc.) 
     # and non-hidden files in the manifest. 
     if File.file?(path) && File.basename(path)[0,1] != '.' 
     manifest << "#{normalize_path(directory, path)}\n" 
     hashes += compute_file_contents_hash(path) if compute_hash 
     end 
    end 

    # Hash the hashes of each file and output as a comment. 
    manifest << "# Hash: #{Digest::MD5.hexdigest(hashes)}\n" if compute_hash 
    manifest << "CACHE MANIFEST\n" 
    manifest.reverse 
    end 

    # Reads the file contents to calculate the MD5 hash, so that if a file is 
    # changed, the manifest is changed too. 
    def self.compute_file_contents_hash(path) 
    hash = '' 
    digest = Digest::MD5.new 
    File.open(path, 'r') do |file| 
     digest.update(file.read(8192)) until file.eof 
     hash += digest.hexdigest 
    end 
    hash 
    end 

    # Strips the directory from the start of path, so that each path is relative 
    # to directory. Add a leading forward slash if not present. 
    def self.normalize_path(directory, path) 
    normalized_path = path[directory.length,path.length] 
    normalized_path = '/' + normalized_path unless normalized_path[0,1] == '/' 
    normalized_path 
    end  
end 

Répondre

3

Je vais supposer que vous avez une méthode qui obtient tous les fichiers, puis calcule le hachage. Appelons cette méthode get_file_hash et définissons-la comme ci-dessous.

def get_file_hash 
    file_hash = {} 
    Find.find(Dir.pwd) do |file| 
    file_hash[file] = compute_hash(File.read(file)) 
    end 
    file_hash 
end 

Comme j'ai répondu précédemment, que nous allons stub Find.find et File.read. Cependant, nous ne suivrons pas la méthode compute_hash puisque vous voulez vérifier le hash du fichier. Nous allons laisser la méthode compute_hash créer le hachage réel sur le contenu du fichier.

describe "#get_file_hashes" 

    ...... 

    before(:each) 
    File.stubs(:find).returns(['file1', 'file2']) 
    File.stubs(:read).with('file1').returns('some content') 
    File.stubs(:read).with('file2').returns('other content') 
    end 

    it "should return the hash for all files" 
@whatever_object.get_file_hashes.should eql({'file1' => "hash you are expecting for 'some content'", 'file2' => "hash you are expecting for 'other content'"}) 
end 

end 

Par souci de simplicité, je suis en train de lire le corps du fichier et en passant que la méthode compute_hash et la génération d'un hachage. Toutefois, si votre méthode compute_hash utilise d'autres méthodes sur le fichier ainsi pour la génération de hachage. Ensuite, vous pouvez simplement les remplacer et renvoyer une valeur à passer à la méthode compute_hash. Bien que, je serais plus tenté de tester la méthode compute_hash séparément s'il s'agit d'une méthode publique et simplement son appel dans la méthode get_file_hash.

Concernant ne pas afficher les fichiers cachés; vous utiliserez une bibliothèque pour cela afin d'omettre les fichiers privés ou aurez votre propre méthode pour cela. Dans le premier cas, vous n'avez pas besoin d'écrire un test (en supposant que la bibliothèque est bien testée) et pour le dernier cas, vous devez tester cette méthode séparée pour celle-ci. Pour tester le re-calcul du hachage des fichiers lorsque leur contenu change; Je suppose que vous devez avoir une sorte d'événement qui déclenche le re calcul de hachage. Il suffit d'appeler cette méthode d'événement et d'affirmer la concordance de hachage du fichier.

+0

Merci. Je viens d'éditer la question pour inclure le code que j'essaie de tester. –

+0

Je serai en train de forcer le manifeste et je vous enverrai une demande de retrait une fois que j'aurai fait les changements. Semble Ok? – nas

+0

Brillant, ça a l'air parfait. Merci! –

1

Est-ce que MockFS vous aider ici? Je vois FS faux a été mentionné en réponse à votre question initiale, mais je ne suis pas sûr que vous pouvez vous moquer du contenu du fichier avec cela.

1

Pourriez-vous mock la valeur retournée par la méthode que vous utilisez pour lire les fichiers? De cette façon, vous pouvez tester la valeur de hachage attendue et au moins s'assurer que les fichiers sont en cours de lecture.

Éditer: Il semble que FakeFS possède une méthode File.read, alors peut-être que cela fonctionnera.