Agile Web Development with Rails, Edition 5

11.1 Iteration F1: Moving the Cart 10.3 Iteration E3: Finishing the Cart

10.4 Playtime

Once again, get the tests working, and add tests for the smarter cart.

See that the tests fail.

rails test
/home/rubys/.rvm/gems/ruby-2.4.0/gems/sass-3.4.23/lib/sass/util.rb:1109: warning: constant ::Fixnum is deprecated
/home/rubys/.rvm/gems/ruby-2.4.0/gems/sass-3.4.23/lib/sass/util.rb:1109: warning: constant ::Fixnum is deprecated
Run options: --seed 29189
 
# Running:
 
....F
 
Failure:
LineItemsControllerTest#test_should_create_line_item [/home/rubys/git/awdwr/edition4/work-240/depot/test/controllers/line_items_controller_test.rb:26]:
<Your Pragmatic Cart> expected but was
<Your Cart>..
Expected 0 to be >= 1.
 
bin/rails test test/controllers/line_items_controller_test.rb:19
 
.....................F
 
Failure:
CartsControllerTest#test_should_destroy_cart [/home/rubys/git/awdwr/edition4/work-240/depot/test/controllers/carts_controller_test.rb:42]:
"Cart.count" didn't change by -1.
Expected: 1
  Actual: 2
 
bin/rails test test/controllers/carts_controller_test.rb:41
 
.
 
Finished in 0.510562s, 54.8416 runs/s, 109.6831 assertions/s.
 
28 runs, 56 assertions, 2 failures, 0 errors, 0 skips

Update the value of the header that we are looking for

edit test/controllers/line_items_controller_test.rb
  test "should create line_item" do
    assert_difference('LineItem.count') do
      post line_items_url, params: { product_id: products(:ruby).id }
    end
 
    follow_redirect!
 
    assert_select 'h2', 'Your Cart'
    assert_select 'td', "Programming Ruby 1.9"
  end

Update expected target of redirect: Cart#destroy.

avoid using session in integration tests

edit test/controllers/carts_controller_test.rb
  test "should destroy cart" do
    post line_items_url, params: { product_id: products(:ruby).id }
    @cart = Cart.find(session[:cart_id])
 
    assert_difference('Cart.count', -1) do
      delete cart_url(@cart)
    end
 
    assert_redirected_to store_index_url
  end

Test both unique and duplicate products.

edit test/models/cart_test.rb
require 'test_helper'
 
class CartTest < ActiveSupport::TestCase
  test "add unique products" do
    cart = Cart.create
    book_one = products(:one)
    book_two  = products(:two)
    cart.add_product(book_one).save!
    cart.add_product(book_two).save!
    assert_equal 2, cart.line_items.size
    assert_equal book_one.price + book_two.price, cart.total_price
  end
  
  test "add_duplicate_product" do
    cart = Cart.create
    ruby_book = products(:ruby)
    cart.add_product(ruby_book).save!
    cart.add_product(ruby_book).save!
    assert_equal 2*book_one.price, cart.total_price
    assert_equal 1, cart.line_items.size
    assert_equal 2, cart.line_items[0].quantity
  end 
end
rails test test/models/cart_test.rb
Run options: --seed 32817
 
# Running:
 
E
 
Error:
CartTest#test_add_duplicate_product:
NameError: undefined local variable or method `book_one' for #<CartTest:0x00000004c8eae8>
    test/models/cart_test.rb:24:in `block in <class:CartTest>'
 
bin/rails test test/models/cart_test.rb:19
 
.
 
Finished in 0.095751s, 20.8876 runs/s, 20.8876 assertions/s.
 
2 runs, 2 assertions, 0 failures, 1 errors, 0 skips

Refactor.

edit test/models/cart_test.rb
require 'test_helper'
 
class CartTest < ActiveSupport::TestCase
  def setup
    @cart  = Cart.create
    @book_one = products(:ruby)
    @book_two  = products(:two)
  end
  
  test "add unique products" do
    @cart.add_product(@book_one).save!
    @cart.add_product(@book_two).save!
    assert_equal 2, @cart.line_items.size
    assert_equal @book_one.price + @book_two.price, @cart.total_price
  end
 
  test "add duplicate product" do
    @cart.add_product(@book_one).save!
    @cart.add_product(@book_one).save!
    assert_equal 2*@book_one.price, @cart.total_price
    assert_equal 1, @cart.line_items.size
    assert_equal 2, @cart.line_items[0].quantity
  end 
end
rails test test/models/cart_test.rb
Run options: --seed 33957
 
# Running:
 
..
 
Finished in 0.090892s, 22.0042 runs/s, 55.0106 assertions/s.
 
2 runs, 5 assertions, 0 failures, 0 errors, 0 skips

Verify that the tests pass.

rails test
Run options: --seed 61855
 
# Running:
 
..............................
 
Finished in 0.436335s, 68.7545 runs/s, 144.3845 assertions/s.
 
30 runs, 63 assertions, 0 failures, 0 errors, 0 skips

Now the tests should pass.

rails test
Run options: --seed 5965
 
# Running:
 
..............................
 
Finished in 0.436692s, 68.6984 runs/s, 144.2666 assertions/s.
 
30 runs, 63 assertions, 0 failures, 0 errors, 0 skips

Add price to line item

rails generate migration add_price_to_line_item price:decimal
      invoke  active_record
      create    db/migrate/20170320044236_add_price_to_line_item.rb
edit db/migrate/20170320044236_add_price_to_line_item.rb
class AddPriceToLineItem < ActiveRecord::Migration[5.1]
  def change
    add_column :line_items, :price, :decimal
    LineItem.all.each do |li|
      li.price = li.product.price
    end
  end
end
rails db:migrate
mv 20170320044236_add_price_to_line_item.rb 20170320000006_add_price_to_line_item.rb
== 20170320000006 AddPriceToLineItem: migrating ===============================
-- add_column(:line_items, :price, :decimal)
   -> 0.0004s
== 20170320000006 AddPriceToLineItem: migrated (0.0301s) ======================
 
edit app/models/cart.rb
class Cart < ApplicationRecord
  has_many :line_items, dependent: :destroy
 
  def add_product(product)
    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)
      current_item.price = current_item.product.price
    end
    current_item
  end
 
  def total_price
    line_items.to_a.sum { |item| item.total_price }
  end
end
git commit -a -m "Adding a Cart"
[master 7f1cc15] Adding a Cart
 4 files changed, 60 insertions(+), 11 deletions(-)
git tag iteration-d

11.1 Iteration F1: Moving the Cart 10.3 Iteration E3: Finishing the Cart