Activity Description

Add unit tests which add unique products and duplicate products. Note that you will need to modify the fixture to refer to products and carts by name, for example product: ruby.

Author’s Solutions

Solution – search for “Test both unique and duplicate products”

Readers’ Solutions

Long says

My attempt:

In test/unit/cart_test.rb:

  def new_cart_with_one_product(product_name)
    cart = Cart.create
    cart.add_product(products(product_name).id)
    cart
  end

  test 'cart should create a new line item when adding a new product' do
    cart = new_cart_with_one_product(:one)
    assert_equal 1, cart.line_items.count
    # Add a new product
    cart.add_product(products(:ruby).id)
    assert_equal 2, cart.line_items.count
  end

  test 'cart should update an existing line item when adding an existing product' do
    cart = new_cart_with_one_product(:one)
    # Re-add the same product
    cart.add_product(products(:one).id)
    assert_equal 1, cart.line_items.count
  end

Arun says

I am doubtful whether the question was about creating a new and duplicate line items or products. I thought the answer is along the lines of


  test "unique_product_should_be_saved" 
    product = Product.new(:title => "unique-title",
                          :description => "a unique product",
                          :image_url => "unique.png",
                          :price => 22)
    assert product.save

  end

  test "non_unique_product_should_error_out" 
    product = Product.new(:title => "unique-title",
                          :description => "a unique product",
                          :image_url => "unique.png",
                          :price => 22)
    assert !product.save
    assert_equal "has  not been saved since it is a duplicate ", product.errors[:title].join('; ')
  end

Doc says

File: test/unit/cart_test.rb

require 'test_helper'

class CartTest < ActiveSupport::TestCase
  test "cart line item should save price" do
    cart = carts(:cart1)
    [:ruby, :loremipsum, :ruby].each do |which_product|
      product = products(which_product)
      item = cart.add_product product.id
      assert_equal item.price, product.price,
        "cart line item did not save price" 
    end
  end

  test "cart line item quantity should increment" do
    cart = carts(:cart2)
    product = products(:ruby)
    multiple = 3
    multiple.times { cart.add_product product.id }
    item = cart.line_items.find_by_product_id product.id    
    assert_equal multiple, item.quantity,
      "cart line item quantity is incorrect" 
  end

  test "cart line item price should equal most-recent price" do
    cart = carts(:cart3)
    product = products(:ruby)
    sale_price = 0.8 * product.price
    [product.price, sale_price, 100].each do |price|
      cart.add_product product.id, price
      item = cart.line_items.find_by_product_id product.id
      assert_equal price, item.price,
        "price #{item.price} is incorrect; should be #{price}" 
    end
  end

  test "cart should be created and destroyed" do
    assert_same Cart.count, Cart.destroy_all.count,
      "unable to destroy existing carts" 
    Cart.create
    assert_equal 1, Cart.count, "unable to create cart" 
    assert_equal 1, Cart.destroy_all.count, "unable to destroy cart" 
  end
end
File: test/fixtures/carts.yml

...
one: {}
cart1: {}
cart2: {}
cart3: {}

Updating Cart

Worth mentioning that there’s an error in one of these previous posts. It should be “assert_equal XXX, @cart.line_items.length” not count…..

Sean says

Doc,

Try using

multiple.times { cart.add_product(product.id).save! }
instead of

multiple.times { cart.add_product product.id }

Kamesh says

I was trying to add an additional test to products_controller_test to test the deletion of product in the cart, a point to note is that when i had the test case as shown below (as mentioned in the Solutions part), my functional tests failed

  test "can't delete a product in cart" do 

    assert_difference('Product.count', 0) do 
     delete :destroy, :id => products(:ruby).to_param
    end

    assert_redirected_to products_path

  end

I tried making the following update to the method, which then made my functional tests to pass

  test "can't delete a product in cart" do 

    assert_difference('Product.count', 0) do 
      cart = Cart.new
      cart.add_product(products(:ruby).id,products(:ruby).price).save!
      delete :destroy, :id => products(:ruby).to_param
    end

    assert_redirected_to products_path

  end

John says

I thought I would share my result

In test/fixtures/carts.yml:

cart1:
  id: 1

cart2:
  id: 2
In test/fixtures/line_items.yml:

o1_coffee:
  product_id: 2
  cart_id: 1
  quantity: 2
  price: 36.00

o1_ruby:
  product_id: 3
  cart_id: 1
  quantity: 2
  price: 49.95

o1_rails:
  product_id: 4
  cart_id: 1
  quantity: 2
  price: 34.95

o2_ruby:
  product_id: 3
  cart_id: 2
  quantity: 1
  price: 49.95
In test/fixtures/products.yml:

one:
  id: 7
  title: One
  description: MyText
  image_url: MyString
  price: 9.99

two:
  id: 6
  title: Two
  description: MyText
  image_url: MyString
  price: 9.99

coffee:
  id: 2
  title: CoffeeScript
  description:
    <p>
    CoffeeScript is JavaScript done right. It provides all of JavaScripts
    functionality wrapped in a cleaner, more succinct syntax. In the first
    book on this exciting new language, CoffeeScript guru Trevor Burnham
    shows you how to hold onto all the power and flexibility of JavaScript
    while writing clearer, cleaner, and safer code.
    </p>
  image_url: cs.jpg
  price: 36.00

ruby:
  id: 3
  title: Programming Ruby 1.9
  description:
    <p>
    Ruby is the fastest growing and most exciting dynamic
    language out there. If you need to get working programs
    delivered fast, you should add Ruby to your toolbox.
    </p>
  image_url: ruby.jpg
  price: 49.95

rails:
  id: 4
  title: Rails Test Prescriptions
  description:
    <p>
    <em>Rails Test Prescriptions</em> is a comprehensive guide to testing
    Rails applications, covering Test-Driven Development from both a
    theoretical perspective (why to test) and from a practical perspective
    (how to test effectively). It covers the core Rails testing tools and
    procedures for Rails 2 and Rails 3, and introduces popular add-ons,
    including Cucumber, Shoulda, Machinist, Mocha, and Rcov.
    </p>
  image_url: rtp.jpg
  price: 34.95

Use assigns to hook instance variables after a POST CREATE.

In test/fixtures/products.yml:

  test "One cart per session - The cart already exists" do
        assert_no_difference('Cart.count') do
      post :create, {product_id: products(:ruby).id}, {'cart_id' => '1'}
        end
        assert_equal "1", session[:cart_id]
        assert_redirected_to store_url
  end

  test "One cart per session - Create a new cart" do
    assert_difference('Cart.count', 1) do
      post :create, product_id: products(:ruby).id
    end
        assert_redirected_to store_url
    end

    test "Validate existing carts" do
        tests = [
      [:coffee, :o1_coffee,"108.00", 'cart1'],
      [:ruby,   :o1_ruby,  "149.85", 'cart1'],
      [:rails,  :o1_rails, "104.85", 'cart1'],
            [:coffee, :o2_coffee, "36.00", 'cart2'],
            [:ruby,   :o2_ruby,   "99.90", 'cart2']
    ].each do |t|

            # Note :o2_coffee doesn't exist
            # Validate t[1] exists in the database or POST created it!
            begin
                quantity = line_items(t[1]).quantity
            rescue
                quantity = 0
            end

            # Validate the products test database (fixture import)
            assert products(t[0])

            # Validate the carts test database (fixture import)
            assert carts(t[3])

            # Validate a product selection does not change the cart
          assert_no_difference('Cart.count') do
        post :create, {product_id: products(t[0]).id}, {'cart_id' => carts(t[3]).id.to_s}
          end

            # Validate the cart id set in the test database (fixture import) is the
            # cart id from post
            # ASSUMES @cart.id not altered in POST!!!! But this should never happen!
          assert_equal carts(t[3]).id, assigns["cart"].id

            # Validate choosing a product added 1 to its quantity
      assert_equal quantity+1, assigns["line_item"].quantity

            # Validate the product line_item total_price increases by the
            # product price - t[2] reflects the price increase
          assert_equal t[2], "%.2f" % assigns["line_item"].total_price
        end
  end