2013-04-19 2 views
2

특정 정규 표현식과 일치하는 파일의 특정 위치에서 파일 시스템을 검색하는 방법이있는 모델이 있습니다. 이것은 after_save 콜백에서 실행됩니다. Rspec 및 FactoryGirl을 사용하여이를 테스트하는 방법을 잘 모르겠습니다. 메소드가 테스트 나 컨트롤러가 아닌 모델에 있기 때문에 FakeFS와 같은 것을 사용하는 방법을 잘 모르겠습니다. FactoryGirl 팩토리에서 시작할 위치를 지정 했으므로 설정 조항에서 테스트로 만든 가짜 디렉토리로 변경할 수 있습니까? 나는 그 디렉토리를 조롱 할 수 있을까? 나는 이것을 할 수있는 여러 가지 방법이있을 것이라고 생각하지만, 가장 의미가있는 것은 무엇입니까?Rspec Ruby on Rails 파일 시스템 테스트 모델

감사합니다.

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 

답변

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은 그 클래스에 대해 하나의 메소드를 호출 할 수 있으며 그 단일 메소드가 호출되는지 여부를 테스트하는 것이 훨씬 쉽습니다.

너무 많이하려고하기 때문에 테스트하기가 너무 어렵습니다. 모든 조건부 논리와 외부 의존성은 테스트하려는 다양한 비트로 조롱을해야한다는 것을 의미합니다.

이것은 큰 주제이며 좋은 대답은이 답변의 범위를 벗어납니다. Wikipedia article on SOLID으로 시작하여 문제를 개별 수업으로 분리하고 작고 구성된 방법을 사용하는 이유에 대해 읽어보십시오. 야구장 아이디어를 얻으려면 하나 이상의 분기 또는 10 줄 이상의 코드가있는 메소드가 너무 큽니다. 약 100 줄이 넘는 클래스가 너무 큽니다.