Agile Web Development with Rails, Edition 4

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.

rake test
Run options: 
 
# Running tests:
 
.....
 
Finished tests in 0.101905s, 49.0652 tests/s, 225.6998 assertions/s.
 
5 tests, 23 assertions, 0 failures, 0 errors, 0 skips
 
ruby -v: ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-linux]
Run options: 
 
# Running tests:
 
.F....................
 
Finished tests in 0.232137s, 94.7716 tests/s, 146.4652 assertions/s.
 
  1) Failure:
CartsControllerTest#test_should_destroy_cart [/home/rubys/git/awdwr/edition4/work-32/depot/test/functional/carts_controller_test.rb:43]:
"Cart.count" didn't change by -1.
<1> expected but was
<2>.
 
22 tests, 34 assertions, 1 failures, 0 errors, 0 skips
 
ruby -v: ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-linux]
Errors running test:functionals! #<RuntimeError: Command failed with status (1): [ruby -I"lib:test" -I"/home/rubys/.rvm/gems/ruby-2.1.5/gems/rake-10.4.2/lib" "/home/rubys/.rvm/gems/ruby-2.1.5/gems/rake-10.4.2/lib/rake/rake_test_loader.rb" "test/functional/**/*_test.rb" ]>

Substitute names of products and carts for numbers

edit test/fixtures/line_items.yml
# Read about fixtures at
# http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html
 
one:
  product: ruby
  cart: one
 
two:
  product: ruby
  cart: one

Update expected target of redirect: Cart#destroy.

edit test/functional/carts_controller_test.rb
  test "should destroy cart" do
    assert_difference('Cart.count', -1) do
      session[:cart_id] = @cart.id
      delete :destroy, id: @cart
    end
 
    assert_redirected_to store_path
  end

Test both unique and duplicate products.

edit test/unit/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.id).save!
    cart.add_product(book_two.id).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.id).save!
    cart.add_product(ruby_book.id).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
ruby -I test test/unit/cart_test.rb
Run options: 
 
# Running tests:
 
E.
 
Finished tests in 0.109410s, 18.2798 tests/s, 18.2798 assertions/s.
 
  1) Error:
CartTest#test_add_duplicate_product:
NameError: undefined local variable or method `book_one' for #<CartTest:0x0000000228a918>
    test/unit/cart_test.rb:24:in `block in <class:CartTest>'
    /home/rubys/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/minitest/unit.rb:1265:in `run'
    /home/rubys/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/test/unit/testcase.rb:17:in `run'
    /home/rubys/git/rails/activesupport/lib/active_support/testing/setup_and_teardown.rb:36:in `block in run'
    /home/rubys/git/rails/activesupport/lib/active_support/callbacks.rb:425:in `_run__1759889392233791162__setup__3435305812265790741__callbacks'
    /home/rubys/git/rails/activesupport/lib/active_support/callbacks.rb:405:in `__run_callback'
    /home/rubys/git/rails/activesupport/lib/active_support/callbacks.rb:385:in `_run_setup_callbacks'
    /home/rubys/git/rails/activesupport/lib/active_support/callbacks.rb:81:in `run_callbacks'
    /home/rubys/git/rails/activesupport/lib/active_support/testing/setup_and_teardown.rb:35:in `run'
    /home/rubys/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/minitest/unit.rb:940:in `block in _run_suite'
    /home/rubys/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/minitest/unit.rb:933:in `map'
    /home/rubys/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/minitest/unit.rb:933:in `_run_suite'
    /home/rubys/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/test/unit.rb:663:in `block in _run_suites'
    /home/rubys/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/test/unit.rb:661:in `each'
    /home/rubys/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/test/unit.rb:661:in `_run_suites'
    /home/rubys/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/minitest/unit.rb:884:in `_run_anything'
    /home/rubys/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/minitest/unit.rb:1092:in `run_tests'
    /home/rubys/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/minitest/unit.rb:1079:in `block in _run'
    /home/rubys/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/minitest/unit.rb:1078:in `each'
    /home/rubys/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/minitest/unit.rb:1078:in `_run'
    /home/rubys/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/minitest/unit.rb:1066:in `run'
    /home/rubys/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/test/unit.rb:27:in `run'
    /home/rubys/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/test/unit.rb:780:in `run'
    /home/rubys/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/test/unit.rb:372:in `block (2 levels) in autorun'
    /home/rubys/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/test/unit.rb:33:in `run_once'
    /home/rubys/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/test/unit.rb:371:in `block in autorun'
 
2 tests, 2 assertions, 0 failures, 1 errors, 0 skips
 
ruby -v: ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-linux]

Refactor.

edit test/unit/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.id).save!
    @cart.add_product(@book_two.id).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.id).save!
    @cart.add_product(@book_one.id).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
ruby -I test test/unit/cart_test.rb
Run options: 
 
# Running tests:
 
..
 
Finished tests in 0.101916s, 19.6241 tests/s, 49.0601 assertions/s.
 
2 tests, 5 assertions, 0 failures, 0 errors, 0 skips
 
ruby -v: ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-linux]

Verify that the tests pass.

rake test
Run options: 
 
# Running tests:
 
.......
 
Finished tests in 0.156900s, 44.6144 tests/s, 178.4577 assertions/s.
 
7 tests, 28 assertions, 0 failures, 0 errors, 0 skips
 
ruby -v: ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-linux]
Run options: 
 
# Running tests:
 
......................
 
Finished tests in 0.232445s, 94.6461 tests/s, 150.5733 assertions/s.
 
22 tests, 35 assertions, 0 failures, 0 errors, 0 skips
 
ruby -v: ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-linux]

Add a test ensuring that non-empty carts can't be deleted.

edit test/functional/products_controller_test.rb
  test "can't delete product in cart" do
    assert_difference('Product.count', 0) do
      delete :destroy, id: products(:ruby)
    end
 
    assert_redirected_to products_path
  end
 
  test "should destroy product" do
    assert_difference('Product.count', -1) do
      delete :destroy, id: @product
    end
 
    assert_redirected_to products_path
  end

Now the tests should pass.

rake test
Run options: 
 
# Running tests:
 
.......
 
Finished tests in 0.132246s, 52.9318 tests/s, 211.7273 assertions/s.
 
7 tests, 28 assertions, 0 failures, 0 errors, 0 skips
 
ruby -v: ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-linux]
Run options: 
 
# Running tests:
 
.......................
 
Finished tests in 0.250786s, 91.7116 tests/s, 147.5361 assertions/s.
 
23 tests, 37 assertions, 0 failures, 0 errors, 0 skips
 
ruby -v: ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-linux]

Add price to line item

rails generate migration add_price_to_line_item price:decimal
      invoke  active_record
      create    db/migrate/20150526044121_add_price_to_line_item.rb
edit db/migrate/20150526044121_add_price_to_line_item.rb
class AddPriceToLineItem < ActiveRecord::Migration
  def change
    add_column :line_items, :price, :decimal
    LineItem.all.each do |li|
      li.price = li.product.price
    end
  end
end
rake db:migrate
mv 20150526044121_add_price_to_line_item.rb 20150526000006_add_price_to_line_item.rb
==  AddPriceToLineItem: migrating =============================================
-- add_column(:line_items, :price, :decimal)
   -> 0.0003s
==  AddPriceToLineItem: migrated (0.0228s) ====================================
 
edit app/models/cart.rb
class Cart < ActiveRecord::Base
  has_many :line_items, dependent: :destroy
 
  def add_product(product_id)
    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 37f4a5c] Adding a Cart
 5 files changed, 74 insertions(+), 11 deletions(-)
git tag iteration-d

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