ruby 如何测试before_destroy回调minitest

qv7cva1a  于 2023-04-05  发布在  Ruby
关注(0)|答案(1)|浏览(107)

昨天我问了一个关于如何使用Minitest单元测试私有方法的问题
我被告知你永远不应该测试私人方法。
所以我把逻辑从控制器中取出,放到模型中,现在看起来像这样

class Store < ApplicationRecord
  attr_accessor :selected_store_id
  
  before_destroy :move_employees_to_selected_store
  
  scope :by_name, -> { order(name: :asc) }

  belongs_to :organization
  has_many :employments

  validates_presence_of :name

  def move_employees_to_selected_store
    return unless selected_store_id.present?
    store = organization.store.find(selected_store_id)
    employments.update_all(store_id: store.id)
  end
end

这样就可以了!(selected_store_id在视图中更新)
但是我遇到了一个问题,因为我不知道如何测试这个方法,因为我不能传入selected_store_id
我正准备尝试以下内容,但该Assert不会是真的!

def test_employments_are_moved_to_selected_store_when_store_is_removed
    u = UserFactory.create
    o = OrganizationFactory.create
    s1 = o.store.create(name: "Test")
    s2 = o.store.create(name: "Test 2")
    s1.employments.create(user: u)
    s1.destroy
    assert_equal s2.employments.count, 1
  end

我如何重构我的测试或回调来传递存储id?

zdwk9cvp

zdwk9cvp1#

你的控制器知道如何挑选一个选中的商店。并且知道它需要在销毁之前这样做。
让控制器实际转移员工具有较少的隐藏逻辑,并且更容易测试。

class Store < ApplicationRecord
  scope :by_name, -> { order(name: :asc) }

  belongs_to :organization
  has_many :employments

  validates_presence_of :name

  def transfer_employees(new_store)
    employments.update_all(store_id: new_store.id)
  end
end

class StoreController
  def destroy # before (I'm guessing)
    @store = Store.find(params[:store_id])
    @store.selected_store_id = params[:selected_store_id]
    @store.destroy
  end

  def destroy # after
    @store = Store.find(params[:store_id])
    @new_store = Store.find(params[:selected_store_id])
    @store.transfer_employees(@new_store)
    @store.destroy
  end
end

class StoreTest
 def test_employments_are_transferred
    u = UserFactory.create
    o = OrganizationFactory.create
    s1 = o.stores.create(name: "Test")
    s2 = o.stores.create(name: "Test 2")
    s1.employments.create(user: u)
    s1.transfer_employees(s2)
    assert_equal s2.employments.count, 1
  end
end

如果您注意到存储销毁开始获取邮件列表和其他逻辑,请将其移动到服务/事务对象。

class StoreController
  def destroy
    @store = Store.find(params[:store_id])
    @new_store = Store.find(params[:selected_store_id])
    DestroyStore.new.destroy_store(@store, @new_store)
  end
end

class DestroyStoreService
  def destroy_store(store1, store2)
    store1.transfer_employees(store2)
    store1.destroy
  end
end

不是最佳的,但您可以将服务层移动到对象中,但我倾向于尽可能将业务逻辑与数据层分离。

class Store
  def destroy_and_transfter(new_store)
    transfer_employees(new_store)
    destroy
  end
end

祝你好运

相关问题