У меня есть модель, в которой есть метод, который просматривает файловую систему, начиная с определенного места, на предмет файлов, соответствующих определенному регулярному выражению. Это выполняется в обратном вызове 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
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 и прочтите оттуда некоторые из обоснование разделения задач на отдельные классы и использования крошечных, составных методов. Чтобы дать вам приблизительное представление, метод с более чем одной ветвью или более чем 10 строками кода слишком велик; класс, содержащий более 100 строк кода, слишком велик.
Похожие вопросы
Новые вопросы
ruby-on-rails
Ruby on Rails - это полнофункциональная платформа веб-приложений с открытым исходным кодом, написанная на Ruby. Он следует популярной модели фреймворка MVC и известен своим подходом «соглашение поверх конфигурации» при разработке приложений.