I’m not sure whether this is a problem of scopes or something, but I’m having problems with updating an object in a test:unit.
The interesting thing is that all functions work perfectly in development mode, but as soon as I switch to rake test:unit, it all breaks.
Here’s the relevant code:
class Cart < ActiveRecord::Base
def add_product(product)
current_item = line_items.find_by_product_id(product.id)
if current_item
Rails::logger.debug "Incrementing quantity"
Rails::logger.debug current_item.quantity
current_item.quantity += 1
Rails::logger.debug current_item.quantity
else
current_item = line_items.build(product_id: product.id,
price: product.price)
end
Rails::logger.debug "Added Product"
Rails::logger.debug current_item.quantity
current_item
end
and the associated test
test "create new cart with one item added twice" do
cart = Cart.new
cart.add_product(products(:one)).save!
assert_equal 1, cart.line_items.size
assert_equal 36.00, cart.total_price
Rails::logger.debug cart.line_items.to_a
cart.add_product(products(:one)).save!
Rails::logger.debug "Added second item"
Rails::logger.debug cart.line_items.to_a
Rails::logger.debug cart.total_price
assert_equal 1, cart.line_items.size
assert_equal 72.00, cart.total_price
end
And here’s the log output:
Incrementing quantity
1
2
Added Product
2
(0.1ms) SAVEPOINT active_record_1
(0.3ms) UPDATE "line_items" SET "quantity" = 2, "updated_at" = '2013-01-18 15:27:06.958210' WHERE "line_items"."id" = 980190963
(0.1ms) RELEASE SAVEPOINT active_record_1
Added second item
[#<LineItem id: 980190963, product_id: 1, cart_id: nil, created_at: "2013-01-18 15:27:06", updated_at: "2013-01-18 15:27:06", quantity: 1, price: 36>]
36
ew quantity (which has changed according to the last log).
Interestingly, within the function itself, the quantity field is updating to two. It even saves to the SQL database. But when I call up cart.line_items, it even has the updated field, but it doesn’t save the new quantity (which has changed according to the last log).
Rails does not have an identity map by default. This means that whenever you do a
LineItem.find(1)and anotherLineItem.find(1)you’ll get two separate objects that are created from the same row of data in your database but do not have any connection between them. If one of them is changed and saved to the database the other object won’t know about it and still have the old data.In your example you’re doing
line_items.find_by_product_id(product.id)which will do a find and return a new object every time. There’s no connection to anyLineItemobjects previously loaded intocart.line_items.There are two options to update the data on stale objects. One of them is the
.reloadmethod which will reload all the data for one object from the database. The other option is passing atrueto thecart.line_itemsassociation. A call like cart.line_items(true) will force a new query to the database to get all line items.The reason that this bug only hits you during test is simple: In the development mode, inserting and reading is usually done in two separate requests and therefore all the objects are freshly loaded from the database every time.