Как смоделировать / заглушить каталог файлов и их содержимое, используя RSpec? - PullRequest
4 голосов
/ 04 июня 2010

Некоторое время назад я спросил " Как проверить получение списка файлов в каталоге с использованием RSpec? ", и хотя я получил пару полезных ответов, я все еще застрял, отсюда и новый вопрос с некоторыми подробностями о том, что я пытаюсь сделать.

Я пишу свой первый RubyGem. Он имеет модуль, который содержит метод класса, который возвращает массив, содержащий список не скрытых файлов в указанном каталоге. Как это:

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

Массив также содержит элемент, который представляет метаданные о файлах. На самом деле это хэш хэшей, сгенерированный из содержимого файлов, идея состоит в том, что изменение даже одного файла меняет хеш.

Я написал свои ожидающие примеры RSpec, но я действительно не знаю, как их реализовать:

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"

Я действительно не хочу, чтобы тесты зависели от реальных файлов, выступающих в качестве фиксаторов. Как я могу издеваться или заглушать файлы и их содержимое ? В реализации gem будет использоваться Find.find, но, как сказал один из ответов на другой мой вопрос, мне не нужно тестировать библиотеку.

Я действительно не знаю, как написать эти спецификации, поэтому любая помощь очень ценится!


Редактировать : метод cache ниже - это метод, который я пытаюсь проверить:

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

Ответы [ 3 ]

4 голосов
/ 05 июня 2010

Я собираюсь предположить, что у вас есть какой-то метод, который получает все файлы и затем вычисляет хеш. Давайте назовем этот метод get_file_hash и определим его следующим образом.

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

Как я уже ответил ранее, мы собираемся заглушить Find.find и File.read. Однако мы не заглушим метод compute_hash, так как вы хотите проверить хеш файла. Мы позволим методу compute_hash создать реальный хэш для содержимого файла.

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

Для простоты я просто читаю тело файла и передаю его методу compute_hash и генерирую хеш. Однако, если ваш compute_hash метод использует некоторые другие методы для файла, а также для генерации хеша. Затем вы можете просто заглушить их и вернуть значение, которое будет передано методу compute_hash. Хотя мне бы хотелось испытать метод compute_hash отдельно, если это открытый метод, и просто заглушить его вызов в методе get_file_hash.

По поводу не показа скрытых файлов; вы либо будете использовать библиотеку для этого, чтобы пропустить личные файлы, либо будете использовать собственный метод, который это делает. В первом случае вам не нужно писать какой-либо тест (при условии, что библиотека хорошо протестирована), а во втором случае вам нужен тест для этого отдельного метода, а не для этого.

Для тестирования повторного вычисления хэша для файлов при изменении их содержимого; Я предполагаю, что у вас должно быть какое-то событие, которое запускает пересчет хэша. Просто вызовите этот метод события и подтвердите совпадение хэша файла.

1 голос
/ 05 июня 2010

Не могли бы вы высмеять значение, возвращаемое любым методом, который вы используете для чтения файлов?Таким образом, вы можете протестировать ожидаемое значение хеш-функции и, по крайней мере, убедиться, что файлы читаются.

Редактировать: похоже, у FakeFS есть метод File.read, так что, возможно, это сработает.

1 голос
/ 04 июня 2010

Помогает ли вам здесь MockFS? http://mockfs.rubyforge.org/

Я вижу, что Fake FS был упомянут в ответе на ваш первоначальный вопрос, но я не уверен, что вы можете смоделировать содержимое файла с этим.

...