You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails模型merge_items方法在RSpec测试中失效问题求助

问题:ShoppingCart的merge_items方法在RSpec测试中未生效

我在ShoppingCart模型中实现了merge_items方法,用于合并拥有相同variant_id的ShoppingCartItem并累加它们的quantity。手动测试时该方法运行正常,但在RSpec测试中似乎被忽略——测试用例中添加两个同variant_id的项后,期望items数量为1,实际得到2。

模型代码

class ShoppingCart < ApplicationRecord
  belongs_to :company
  belongs_to :user
  has_many :items, class_name: "ShoppingCartItem", dependent: :destroy
  accepts_nested_attributes_for :items, reject_if: proc { |attributes| attributes['quantity'].blank? }

  before_validation :remove_invalid_items
  before_validation :merge_items

  def merge_items
    self.items = items.group_by { |i| i[:variant_id] }.map do |variant_id, is|
      quantity_sum = is.sum { |i| i[:quantity] }
      ShoppingCartItem.new(variant_id: variant_id, quantity: quantity_sum)
    end
  end
end

测试代码

require 'rails_helper'

RSpec.describe ShoppingCart, type: :model do
  describe "associations" do
    it { is_expected.to belong_to(:company) }
    it { is_expected.to belong_to(:user) }
    it { is_expected.to have_many(:items) }
  end

  describe "merge_items" do
    let(:shopping_cart) { create(:shopping_cart) }

    it "merge items if same variant_id" do
        existing_item = create(:shopping_cart_item, shopping_cart: shopping_cart, variant_id: "same variant_id", quantity: 1)
        item = create(:shopping_cart_item, shopping_cart: shopping_cart, variant_id: "same variant_id", quantity: 1)

        expect(shopping_cart.reload.items.count).to eq(1)
    end

    it "not merge items if variant_id different" do
      existing_item = create(:shopping_cart_item, shopping_cart: shopping_cart, variant_id: "variant_id", quantity: 1)
      item = create(:shopping_cart_item, shopping_cart: shopping_cart, variant_id: "different variant_id", quantity: 1)

      expect(shopping_cart.reload.items.count).to eq(2)
    end
  end
end

测试失败信息

Failure/Error: expect(shopping_cart.reload.items.count).to eq(1)

   expected: 1
        got: 2

原因分析

测试中直接用create(:shopping_cart_item)创建关联项的方式,是直接向数据库插入记录,不会触发ShoppingCart模型的before_validation回调——回调只有在ShoppingCart实例本身执行验证操作(比如save、update、valid?)时才会触发。当前测试流程中没有触发购物车的验证,所以merge_items方法根本没被调用。

解决方案

方案1:触发购物车的保存/验证操作

在创建完子项后,调用购物车的save或valid?方法触发回调,再重新加载验证结果:

it "merge items if same variant_id" do
  create(:shopping_cart_item, shopping_cart: shopping_cart, variant_id: "same variant_id", quantity: 1)
  create(:shopping_cart_item, shopping_cart: shopping_cart, variant_id: "same variant_id", quantity: 1)

  shopping_cart.save # 触发before_validation回调
  expect(shopping_cart.reload.items.count).to eq(1)
end

方案2:通过购物车关联添加项(更贴合实际业务流程)

直接通过购物车的items关联构建子项,再保存购物车,确保回调被触发:

it "merge items if same variant_id" do
  shopping_cart.items.build(variant_id: "same variant_id", quantity: 1)
  shopping_cart.items.build(variant_id: "same variant_id", quantity: 1)
  
  shopping_cart.save
  expect(shopping_cart.reload.items.count).to eq(1)
end

方案3:使用嵌套属性更新(模拟前端传参场景)

通过嵌套属性更新购物车,更贴近实际业务中添加购物车项的流程:

it "merge items if same variant_id" do
  shopping_cart.update(items_attributes: [
    { variant_id: "same variant_id", quantity: 1 },
    { variant_id: "same variant_id", quantity: 1 }
  ])
  
  expect(shopping_cart.reload.items.count).to eq(1)
end

内容的提问来源于stack exchange,提问作者Mohamed Bengrich

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 17:45:28