如何使用 Rspec 和 FactoryGirl 在 rails 模型中测试 before_update 回调?
How to test before_update callback in rails model with Rspec and FactoryGirl?
我正在尝试测试下面模型的 before_update 回调。
models/option.rb:
class Option < ApplicationRecord
belongs_to :activity
has_many :suboptions, class_name: "Option", foreign_key: "option_id"
belongs_to :parent, class_name: "Option", optional: true, foreign_key: "option_id"
accepts_nested_attributes_for :suboptions, allow_destroy: true,
reject_if: ->(attrs) { attrs['name'].blank? }
validates :name, presence: true
before_create :set_defaults
before_update :set_updates
def set_defaults
self.suboptions.each do |sbp|
sbp.activity_id = self.activity_id
end
end
def set_updates
suboptions.each do |child|
child.activity_id = self.activity_id
end
end
end
规格/models/option.rb:
require 'rails_helper'
RSpec.describe Option, type: :model do
describe "Callbacks" do
it "before_create" do
suboption = create(:option)
option = create(:option, suboptions:[suboption])
option.run_callbacks(:create) {false}
expect(option.suboptions.first.activity_id).to eq suboption.activity_id
end
it "before_update" do
end
end
end
我成功地测试了 before_create 回调(至少它给了我正确的结果)。但是我不知道如何测试 before_update 回调。有办法吗?
好的。我会尝试从头开始。
要测试回调,您必须测试它是否会在应该调用的时候调用。就这样。
您可能想要准确测试该方法的代码。但是这样的方法通常是私有的,而且它们确实应该是私有的。而且您根本不应该测试私有方法的代码。如果您无论如何都想这样做,您的测试将与您的私有方法耦合,这并不好。
您可以这样测试 before_update :set_updates
:
let(:option) { Option.create("init your params here") }
it "test callback" do
expect(option).to receive(:set_updates)
option.save
end
如果你想测试你的私有方法的代码,你可以这样做
let(:option) { Option.create("init your params here") }
it "test callback" do
# expect to receive some messages
# which are in your method code
# for example
expect_any_instance_of(Suboption).to receive(:activity_id=)
option.send(:set_updates)
end
P.S. 你可能想要 watch/listen "Rails Conf 2013 The Magic Tricks of Testing by Sandi Metz"。很有帮助。
警告:这个答案是自以为是的。
测试行为,而不是实施。
回调是一个实现细节。不要直接测试它。相反,假装您不知道模型的内部工作原理,并测试它的行为方式。
如果我没看错代码,行为可以这样描述:
When updating an option, the activity_id of each of its suboptions
is set to the activity_id of the option.
创建一个带有子选项的选项。更新它,重新加载它,并检查每个 activity_id 的值是否正确。
这会比模拟慢,但不那么脆弱。此外,测试更容易编写和维护。
我使用 run_callbacks
找到了解决方案。我创建了一个选项和一个子选项。然后我更新了选项的 activity_id 并使用 option.run_callbacks(:update) {false}
到 运行 before_update
回调(如果我使用 {true}
,它会 运行 before_update
和 after_update
回调):
it "before_update" do
suboption = create(:option)
option = create(:option, suboptions:[suboption])
option.update(activity_id: 5)
option.run_callbacks(:update) {false}
expect(option.suboptions.first.activity_id).to eq option.activity_id
end
如果我不使用 option.run_callbacks(:update) {false}
,expect 表达式会为选项和子选项获得不同的 activity_id。但是通过使用这样的代码,测试 运行s 正确并且选项和子选项具有相同的 activity_id.
我正在尝试测试下面模型的 before_update 回调。
models/option.rb:
class Option < ApplicationRecord
belongs_to :activity
has_many :suboptions, class_name: "Option", foreign_key: "option_id"
belongs_to :parent, class_name: "Option", optional: true, foreign_key: "option_id"
accepts_nested_attributes_for :suboptions, allow_destroy: true,
reject_if: ->(attrs) { attrs['name'].blank? }
validates :name, presence: true
before_create :set_defaults
before_update :set_updates
def set_defaults
self.suboptions.each do |sbp|
sbp.activity_id = self.activity_id
end
end
def set_updates
suboptions.each do |child|
child.activity_id = self.activity_id
end
end
end
规格/models/option.rb:
require 'rails_helper'
RSpec.describe Option, type: :model do
describe "Callbacks" do
it "before_create" do
suboption = create(:option)
option = create(:option, suboptions:[suboption])
option.run_callbacks(:create) {false}
expect(option.suboptions.first.activity_id).to eq suboption.activity_id
end
it "before_update" do
end
end
end
我成功地测试了 before_create 回调(至少它给了我正确的结果)。但是我不知道如何测试 before_update 回调。有办法吗?
好的。我会尝试从头开始。
要测试回调,您必须测试它是否会在应该调用的时候调用。就这样。
您可能想要准确测试该方法的代码。但是这样的方法通常是私有的,而且它们确实应该是私有的。而且您根本不应该测试私有方法的代码。如果您无论如何都想这样做,您的测试将与您的私有方法耦合,这并不好。
您可以这样测试 before_update :set_updates
:
let(:option) { Option.create("init your params here") }
it "test callback" do
expect(option).to receive(:set_updates)
option.save
end
如果你想测试你的私有方法的代码,你可以这样做
let(:option) { Option.create("init your params here") }
it "test callback" do
# expect to receive some messages
# which are in your method code
# for example
expect_any_instance_of(Suboption).to receive(:activity_id=)
option.send(:set_updates)
end
P.S. 你可能想要 watch/listen "Rails Conf 2013 The Magic Tricks of Testing by Sandi Metz"。很有帮助。
警告:这个答案是自以为是的。
测试行为,而不是实施。
回调是一个实现细节。不要直接测试它。相反,假装您不知道模型的内部工作原理,并测试它的行为方式。
如果我没看错代码,行为可以这样描述:
When updating an option, the activity_id of each of its suboptions is set to the activity_id of the option.
创建一个带有子选项的选项。更新它,重新加载它,并检查每个 activity_id 的值是否正确。
这会比模拟慢,但不那么脆弱。此外,测试更容易编写和维护。
我使用 run_callbacks
找到了解决方案。我创建了一个选项和一个子选项。然后我更新了选项的 activity_id 并使用 option.run_callbacks(:update) {false}
到 运行 before_update
回调(如果我使用 {true}
,它会 运行 before_update
和 after_update
回调):
it "before_update" do
suboption = create(:option)
option = create(:option, suboptions:[suboption])
option.update(activity_id: 5)
option.run_callbacks(:update) {false}
expect(option.suboptions.first.activity_id).to eq option.activity_id
end
如果我不使用 option.run_callbacks(:update) {false}
,expect 表达式会为选项和子选项获得不同的 activity_id。但是通过使用这样的代码,测试 运行s 正确并且选项和子选项具有相同的 activity_id.