为什么这个 Rails Minitest assert_equal 因这些预期值和实际值而失败?

Why is this Rails Minitest assert_equal failing with these Expected and Actual values?

我有;

在 Post 到 line_items_path 之后,product_id 被传递给 line_items_controller#create(如下所示),其中 cart#add_product 被调用(如下),它确定是否已经存在具有相同 product_id 的 line_item。如果是,则现有 line_item 的 line_item.quantity 增加 1,如果不是,则 line_item.build 然后调用 #save 以创建一个全新的行项目。

line_items_controller.rb;

def create
  product = Product.find(params[:product_id])
  @line_item = @cart.add_product(product.id, product.price)
  respond_to do |format|
    if @line_item.save
      format.html { redirect_to @line_item.cart } #, notice: 'Line item was successfully created.'
      format.json { render :show, status: :created, location: @line_item }
    else
      format.html { render :new }
      format.json { render json: @line_item.errors, status: :unprocessable_entity }
    end
  end
end

这里是来自cart.rb;

的#add_product
def add_product(product_id, product_price)
    current_item = line_items.find_by(product_id: product_id)
    if current_item
        current_item.quantity += 1
    else
        current_item = line_items.build(product_id: product_id, price: product_price)
    end
    current_item
end

我正在尝试测试此功能(通过 minitest),但无法理解我看到的行为。我的测试来了;

test/controllers/carts_

test "should update quantity of existing line item when adding another of the same product" do
    cart = Cart.create 
    cart.add_product(products(:product_one).id, products(:product_one).price)
    cart.add_product(products(:product_one).id, products(:product_one).price)
    assert_equal cart.line_items.size, 1 
    # assert_equal cart.line_items[0].quantity, 2
  end

测试失败,反馈;

Failure:
CartsControllerTest#test_should_update_quantity_of_existing_line_item_when_adding_another_of_the_same_product [AgileWebDev/depot/test/controllers/carts_controller_test.rb:73]:
Expected: 2
  Actual: 1

我不明白,因为

a) 我已经在开发服务器上测试了行为并通过 psql 查看了表格,它的行为符合预期。

b) 我的测试断言; assert_equal cart.line_items.size, 1 预期为 1,那么为什么 Minitest 错误消息状态为 Expected: 2

我很困惑,经过几个小时的摸索和阅读,我无法理解我在这里做错了什么,有人可以帮忙吗?

注意 - 这是来自敏捷 Web 开发第 10 章末尾定义的额外 'playtime' 任务 Rails 5'。

a) 在您的测试用例中,购物车中没有 line_items 因为它是刚刚创建的,所以下面的行将始终 return nil:

current_item = line_items.find_by(product_id: product_id)

因此,下面的行被调用了两次,这导致 line_items.size 为 2:

current_item = line_items.build(product_id: product_id, price: product_price)

您可以使用缓存变量来避免它:

def add_product(product_id, product_price)
    @current_item ||= line_items.find_by(product_id: product_id)
    if @current_item
        @current_item.quantity += 1
    else
        @current_item = line_items.build(product_id: product_id, price: product_price)
    end
    @current_item
end

b) 根据Rails's Minitest的介绍,断言的格式应该是

assert_equal( expected, actual, [msg] )

这就是你的断言信息出错的原因。正确的是

assert_equal 1, cart.line_items.size, "should has only 1 item"

针对 b 部分的回应:如果您查看 assert_equal 的 APIdocks,预期值首先出现,然后是实际值。所以你写的 1 被 Minitest 当作实际值,而期望值是 cart.line_items.size

我不确定为什么 cart.line_items.size 等于 B。假设您对开发服务器和 psql 的观察是正确的,这可能与您的测试条件设置方式有关。如果在添加行项目的代码之前添加 assert_equal 0, cart.line_items.size 会发生什么情况?

test "should update quantity of existing line item when adding another of the same product" do
    cart = Cart.create 
    assert_equal 0, cart.line_items.size
    cart.add_product(products(:product_one).id, products(:product_one).price)
    cart.add_product(products(:product_one).id, products(:product_one).price)
    assert_equal cart.line_items.size, 1 
    # assert_equal cart.line_items[0].quantity, 2
end