如何使用Minitest测试before_destroy回调并传入selected_store_id?
问题描述
昨天我询问了如何用Minitest测试私有方法,得到了不应测试私有方法的建议,于是把逻辑从控制器移到了Store模型,代码如下:
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,我写的测试用例如下:
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?
解决方案
方法一:直接在测试中设置selected_store_id
既然selected_store_id是用attr_accessor定义的实例变量,测试时直接给s1赋值即可:
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) # 直接指定目标门店ID s1.selected_store_id = s2.id s1.destroy assert_equal s2.employments.count, 1 end
这样回调执行时就能拿到selected_store_id,完成员工转移,断言即可通过。
方法二:优化回调逻辑(可选)
如果觉得依赖实例变量不够直观,可以给模型新增带参数的销毁方法,明确转移目标:
# Store模型新增方法 def destroy_with_transfer(target_store_id) self.selected_store_id = target_store_id destroy end
测试时调用这个方法:
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_with_transfer(s2.id) assert_equal s2.employments.count, 1 end
另外注意:模型中organization.store.find应该修正为organization.stores.find(has_many关联对应复数形式),避免实际运行时出现错误。
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

