2

我有一个模型,该模型具有一种方法,该方法从特定位置开始查找与特定正则表达式匹配的文件的文件系统。这是在 after_save 回调中执行的。我不确定如何使用 Rspec 和 FactoryGirl 进行测试。我不确定如何使用 FakeFS 之类的东西,因为该方法在模型中,而不是在测试或控制器中。我指定了在我的 FactoryGirl 工厂中启动的位置,所以我可以将其更改为在设置子句中由测试创建的假目录?我可以模拟目录吗?我认为可能有几种不同的方法可以做到这一点,但哪种方法最有意义?

谢谢!

  def ensure_files_up_to_date
    files = find_assembly_files
    add_files = check_add_assembly_files(files)
    errors = add_assembly_files(add_files)
    if errors.size > 0 then
      return errors
    end
    update_files = check_update_assembly_files(files)
    errors = update_assembly_files(update_files)
    if errors.size > 0 then
      return errors
    else
      return []
    end
  end

 def find_assembly_files
    start_dir = self.location
    files = Hash.new
    if ! File.directory? start_dir then
      errors.add(:location, "Directory #{start_dir} does not exist on the system.")
      abort("Directory #{start_dir} does not exist on the system for #{self.inspect}")
    end
    Find.find(start_dir) do |path|
      filename = File.basename(path).split("/").last
      FILE_TYPES.each { |filepart, filehash|
        type = filehash["type"]
        vendor = filehash["vendor"]
        if filename.match(filepart) then
          files[type] = Hash.new
          files[type]["path"] = path
          files[type]["vendor"] = vendor
        end
      }
    end
    return files
  end

  def check_add_assembly_files(files=self.find_assembly_files)
    add = Hash.new
    files.each do |file_type, file_hash|
      # returns an array
      file_path = file_hash["path"]
      file_vendor = file_hash["vendor"]
      filename = File.basename(file_path)
      af = AssemblyFile.where(:name => filename)

      if af.size == 0 then
        add[file_path] = Hash.new
        add[file_path]["type"] = file_type
        add[file_path]["vendor"] = file_vendor
      end
    end
    if add.size == 0 then
      logger.error("check_add_assembly_files did not find any files to add")
      return []
    end
    return add
  end

  def check_update_assembly_files(files=self.find_assembly_files)
    update = Hash.new
    files.each do |file_type, file_hash|
      file_path = file_hash["path"]
      file_vendor = file_hash["vendor"]
      # returns an array
      filename = File.basename(file_path)
      af = AssemblyFile.find_by_name(filename)

      if !af.nil? then
        if af.location != file_path or af.file_type != file_type then
          update[af.id] = Hash.new
          update[af.id]['path'] = file_path
          update[af.id]['type'] = file_type
          update[af.id]['vendor'] = file_vendor
        end
      end
    end
    return update
  end

 def add_assembly_files(files=self.check_add_assembly_files)
    if files.size == 0 then
      logger.error("add_assembly_files didn't get any results from check_add_assembly_files")
      return []
    end
    asm_file_errors = Array.new
    files.each do |file_path, file_hash|
      file_type = file_hash["type"]
      file_vendor = file_hash["vendor"]
      logger.debug "file type is #{file_type} and path is #{file_path}"
      logger.debug FileType.find_by_type_name(file_type)
      file_type_id = FileType.find_by_type_name(file_type).id
      header = file_header(file_path, file_vendor)
      if file_vendor == "TBA" then
        check = check_tba_header(header, file_type, file_path)
        software = header[TBA_SOFTWARE_PROGRAM]
        software_version = header[TBA_SOFTWARE_VERSION]
      elsif file_vendor == "TBB" then
        check = check_tbb_header(header, file_type, file_path)
        if file_type == "TBB-ANNOTATION" then
          software = header[TBB_SOURCE]
        else
          software = "Unified"
        end
        software_version = "UNKNOWN"
      end

      if check == 0 then
        logger.error("skipping file #{file_path} because it contains incorrect values for this filetype")
        asm_file_errors.push("#{file_path} cannot be added to assembly because it contains incorrect values for this filetype")
        next
      end

      if file_vendor == "TBA" then
        xml = header.to_xml(:root => "assembly-file")
      elsif file_vendor == "TBB" then
        xml = header.to_xml
      else
        xml = ''
      end

      filename = File.basename(file_path)
      if filename.match(/~$/) then
        logger.error("Skipping a file with a tilda when adding assembly files.  filename #{filename}")
        next
      end
      assembly_file = AssemblyFile.new(
                    :assembly_id => self.id,
                    :file_type_id => file_type_id,
                    :name => filename,
                    :location => file_path,
                    :file_date => creation_time(file_path),
                    :software => software,
                    :software_version => software_version,
                    :current => 1,
                    :metadata => xml
                )

      assembly_file.save! # exclamation point forces it to raise an error if the save fails
    end # end files.each

    return asm_file_errors
  end
4

1 回答 1

1

快速回答:您可以像其他任何方法一样存根模型方法。存根模型的特定实例,然后存根find或任何返回它的东西,或者any_instance如果您不想担心涉及哪个模型,则存根。就像是:

it "does something" do
  foo = Foo.create! some_attributes
  foo.should_receive(:some_method).and_return(whatever)
  Foo.stub(:find).and_return(foo)
end

真正的答案是您的代码太复杂而无法有效测试。您的模型甚至不应该知道文件系统的存在。该行为应该封装在其他类中,您可以独立测试这些类。然后,您的模型after_save可以只调用该类上的单个方法,并且测试该单个方法是否被调用会容易得多。

你的方法也很难测试,因为他们试图做的太多了。所有这些条件逻辑和外部依赖意味着您必须进行大量模拟才能获得您可能想要测试的各个位。

这是一个很大的话题,一个好的答案远远超出了这个答案的范围。从关于 SOLID 的 Wikipedia 文章开始,并从那里阅读将关注点分成单个类并使用微小的组合方法背后的一些原因。给你一个大概的想法,一个有多个分支或超过 10 行代码的方法太大了;超过 100 行代码的类太大了。

于 2013-04-19T21:51:27.937 回答