【问题标题】:Keeping model records and associated directories in sync (with tests)保持模型记录和相关目录同步(与测试)
【发布时间】:2013-11-30 09:08:12
【问题描述】:

在我的应用程序中,我在创建主题记录时创建了一个目录。这是存储与主题相关的文件资产。我一直在努力使目录的存在与记录的生命周期保持同步。这是我目前的看法:

after_create :create_theme_directory
after_rollback :destroy_theme_directory, :on => :create, :if => :id

def directory
    Rails.configuration.uploads_root.join('themes', id.to_s)
end

private

def create_theme_directory
    FileUtils.mkdir_p directory
end

def destroy_theme_directory
    FileUtils.remove_dir directory, :force => true
end

它运行良好,只是 Rspec 在测试后回滚主题记录时似乎不会触发删除目录。

这种事情有最佳实践吗?这个想法是永远不应该留下一个没有相关记录的杂散目录。

【问题讨论】:

  • 可能有更好的方法,但您可以使用 after(:all) 为创建的对象显式调用销毁主题目录。
  • 确实如此。问题是我的初始数据库种子包含一个主题,所以我必须有选择地删除其余的。我想只要我能预测到那个特定初始主题的 ID(可能是 1!)就可以了。
  • 您使用的是before(:all)吗? relishapp.com/rspec/rspec-rails/docs/transactions
  • 我不太确定我会用它做什么。你在想什么? :)
  • 哦,我明白了,我正在使用 factory_girl 创建主题,以便它们在回滚的事务中。我认为只是回滚不是通过rails调用的,所以rails不知道触发回调。

标签: ruby-on-rails activerecord rspec ruby-on-rails-3.2 filesystems


【解决方案1】:

您定义的after_rollback 回调只有在创建、销毁或更新记录是通过 ActiveRecord 完成时才会被调用。当 RSpec 重置时,它不会通过 ActiveRecord,因此不会触发任何事务回调(after_rollback 和 after_commit)。

如果目录仍然存在,您可以添加另一个销毁目录的回调:

after_commit :destroy_theme_directory, :on => :destroy

def destroy_theme_directory
  if File.directory?(directory)
    FileUtils.remove_dir directory, :force => true
  end
end

然后触发您的功能规范中的创建和销毁操作:

scenario 'create and destroy' do
  visit new_directory_path
  #fill_in fields
  click_button "Create"

  expect(page).to have_content "created"

  visit users_path
  click_link "Delete" #assuming only directory object exists and you have a delete link in your directory index page
end

通过这种方式,您可以触发规范中的创建和销毁操作,因此您无需进行任何清理。

另一种选择是手动删除规范中您测试其创建的目录。

#assuming you have model spec for testing that directory is created
it 'creates corresponding directory'
  directory.create
  expect(File.directory?(directory)).to eq true

  # the line below is just for cleanup. No need to do it in an after_all block if it only needs to be done for a few specs
  FileUtils.remove_dir directory, :force => true 
end

希望对您有所帮助。

【讨论】:

  • 这样做的问题是,如果销毁失败(在数据库中),目录将被删除。不一定是坏事,但它破坏了一致性。目录删除失败的可能性较小,这就是为什么我在确定数据库条目消失后运行它的原因。
  • 这是有道理的。您在其中测试目录创建的规范是功能/请求还是模型规范?顺便说一句,我更新了我的答案。
  • 感谢 Gjaldon,您的回答很棒,因为它准确地描述了正在发生的事情。我会奖励你积分:) 目录创建的测试发生在模型规范中,但目录也会在我测试主题的任何地方偶然创建。也许有一种方法可以通过活动记录手动触发回调。我会调查一下。
【解决方案2】:

非常有趣的问题,我自己对此感兴趣,因为我目前正在开发一个正在上传和转换文件的应用程序。

根本不是 RSpec 方面的专家,但我认为它非常面向数据库,如果它与数据库无关(如创建文件夹/更新图像/启动外部接口)。

找到这个关于如何使用 after(:all) 进行清理的示例 with RSepc and carrierwave testing - 与我最初的类似。

另一种选择更实用,但在测试方面可能更有限。 准备好目标结构的备份文件,并在测试完成后恢复它。

如果有人有更好的方法,我确实很感兴趣。

希望这会有所帮助! 欧根

【讨论】:

  • 谢谢。我希望rails可能有一种方法来确认rspec的回滚调用并执行回调。我猜通常会引发错误并且 rails 会处理回滚(调用回调),但 rspec 只是在数据库上手动调用它(绕过 rails)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-29
  • 2011-12-12
  • 2019-02-20
  • 1970-01-01
  • 2021-03-12
  • 1970-01-01
  • 2012-12-04
相关资源
最近更新 更多