拥有大型模型堆栈并广泛使用玩偶缓存技术,最终会在模型更新后“触摸”许多父模型。
在测试时,除非您尝试专门测试该功能,否则这似乎是浪费时间。
有没有办法阻止模型touch
他们的belongs_to
关联用于测试环境或测试级别?
更新1:
我对案件的第一次尝试将是
# /config/initializers/extensions.rb
#
class ActiveRecord::Base
def self.without_touch_for_association(association_name, &block)
association_name = association_name.to_sym
association = self.reflect_on_all_associations(:belongs_to).select { |reflection| reflection.name == association_name }.first
options = association.options
association.instance_variable_set :@options, options.except(:touch)
yield
association.instance_variable_set :@options, options
end
end
Post.without_touch_for_association(:user) do
Post.last.save
end
当然,没有成功,保存Post.last
仍然触及User
。
更新理由:
我理解并同意这种方法可能是错误的根源,而且根本不是一个好的做法。问题是我有一个巨大的套件,有很多集成和单元测试。玩偶缓存也深入到模型树中。每当我查看日志时,我都会看到大量与触摸相关的查询。我知道最好的方法是优化单元测试以增加更多的模拟和存根以及更少的持久性。在集成测试中解决问题更加困难。
无论如何,我是为了学习和研究而提出这个问题。我有兴趣探索这种技术的潜在速度改进。
解决方案:请参阅下面我自己的答案,了解工作代码。
答案 0 :(得分:10)
假设您使用的是Rails 4.1.4或更高版本:
User.no_touching do
Post.last.save
end
甚至
ActiveRecord::Base.no_touching do
Post.last.save
end
答案 1 :(得分:3)
我不同意为测试目的而更改代码的概念。从我的观点来看,测试应该是一个独立的程序。
正如我所看到的那样,您应该为测试套件提供一种方法,仅在某些情况下改变模型的行为。
以下代码
class Book < ActiveRecord::Base
belongs_to :author, touch: true
end
class Author < ActiveRecord::Base
has_many :books
end
您的情况将定义实例方法
belongs_to_touch_after_save_or_destroy_for_author
在Book
课程的幕后。
(感谢AR http://apidock.com/rails/ActiveRecord/Associations/Builder/BelongsTo/add_touch_callbacks)
因此,在您的测试代码中,您可以覆盖该方法以执行不同的操作或根本不执行任何操作!
在我的情况下,我使用Rspec和FactoryGirl,所以我做的是为Book
类创建一个特殊的工厂特性,为该对象重新定义belongs_to_touch_after_save_or_destroy_for_author
FactoryGirl.define do
factory :book do
...
...
end
trait :no_touch do
before(:create) do |book_no_touch|
def book_no_touch.belongs_to_touch_after_save_or_destroy_for_author
true
end
end
end
end
这样,当你需要测试触摸相关对象无关紧要的东西时,可以用该工厂创建一个书对象
book = FactoryGirl.create(:book, :no_touch)
答案 2 :(得分:3)
对于Rails&gt; = 4.2
感谢@Dorian,在Rails 4.2中,可以使用ActiveRecord::NoTouching。
对于Rails&lt; 4.2 强>
我在rspec支持文件中的工作代码:
# /spec/support/active_record_extensions.rb
class ActiveRecord::Base
def self.without_touch_for_association(association, &block)
method_name = :"belongs_to_touch_after_save_or_destroy_for_#{association}"
return unless self.instance_methods.include?(method_name)
method = self.send(:instance_method, method_name)
self.send(:define_method, method_name) { true }
yield
self.send(:define_method, method_name, method)
nil
end
def self.disable_touch_associations!
associations = self.reflect_on_all_associations(:belongs_to)
associations.each do |association|
self.without_touch_for_association association.name do
return
end
end
nil
end
end
将此添加到./spec/spec_helper.rb
以禁用针对整个测试套件定义的任何模型的所有触摸调用:
RSpec.configure do |config|
if ENV['SILENCE_TOUCHES']
config.before :suite do
ActiveRecord::Base.descendants.each {|model| model.disable_touch_associations! }
end
end
end
在特定测试中暂时禁用模型和关联的触摸。
Post.without_touch_for_association(:user) do
Post.last.save
end
感谢下面的@xlembouras指出我正确的方向!
我在测试中使用此功能,我注意到测试套件速度降低了25%,对于30分钟的测试套件。经过更深入的研究后,我可能会发布更准确的结果。
答案 3 :(得分:0)
我不确定这是否可行,但您可以尝试以下方法:
belongs_to :foo, touch: APP_CONFIG['doll_touch']
其中APP_CONFIG是在此guide之后设置的应用程序参数。
因此,在配置的生产/开发部分,您将doll_touch
设置为true
,将测试设置为false
。