4

我正在尝试测试下面模型的 before_update 回调。

模型/选项.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

规格/模型/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 回调。有没有办法做到这一点?

4

3 回答 3

12

警告:这个答案是固执己见的。

测试行为,而不是实现。

回调是一个实现细节。不要直接测试。相反,假装你不知道模型内部是如何工作的,并测试它的行为方式。

如果我正确阅读了代码,则可以这样描述行为:

更新选项时,其每个子选项的 activity_id 都设置为选项的 activity_id。

创建一个带有子选项的选项。更新它,重新加载它,并检查每个 activity_id 的值是否正确。

这将比嘲笑慢,但不那么脆弱。此外,测试更容易编写和维护。

于 2017-01-12T02:37:57.753 回答
6

行。我会尝试从头开始。

要测试回调,您必须测试它应该在什么时候被调用。就这样。

您可能想要准确地测试该方法的代码。但是这样的方法通常是私有的,它们确实应该是私有的。而且您根本不应该测试私有方法的代码。如果您无论如何都想这样做,您的测试将与您的私有方法耦合,这并不好。

您可以像这样测试 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

PS您可能想观看/收听“Rails Conf 2013 The Magic Tricks of Testing by Sandi Metz”。这是非常有帮助的事情。

于 2017-01-11T12:56:12.730 回答
2

我得到了一个使用run_callbacks. 我创建了一个选项和一个子选项。然后我更新了选项的activity_id并用于option.run_callbacks(:update) {false}运行before_update回调(如果我使用{true},它将运行before_updateafter_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 表达式会为选项和子选项获得不同的活动 ID。但是通过使用这样的代码,测试可以正确运行并且选项和子选项具有相同的activity_id。

于 2017-01-12T04:02:37.300 回答