Welcome to your third unit for this course dedicated to practicing Test Driven Development (TDD) utilizing Ruby and RSpec. In this lesson, we will explore the process of building a ShoppingCart
class with multiple features using TDD.
The aim is to provide you with an understanding of how requirements are translated into tests, which are tackled one at a time. You'll observe how tests are written and code is implemented to pass each test, simulating a real-world TDD environment. Previously, tests were provided, but this time, the focus will be on creating them independently.
Remember to use the core concepts of the Red-Green-Refactor cycle while practicing these exercises. I'm still here to help! Just ask if you encounter any questions or issues.
In this section, you will learn how to build the ShoppingCart
class by implementing the following features:
- Starting with an Empty Cart
- Adding a Single Item
- Adding Multiple Items
- Handling Multiple Quantities of the Same Item
- Removing an Item
- Description: When a new shopping cart is created, it should start without any items, and the total price should be zero.
- Details:
- Initialize a new shopping cart using the
ShoppingCart
constructor. - Ensure the
item_count
method returns0
for an empty cart. - Verify the
total
method returns0
for the initial state.
- Initialize a new shopping cart using the
- Examples: A newly created cart should have a count of
0
and a total of0
.
Ruby1class ShoppingCart 2 attr_reader :items # Allow access to items for external methods 3 4 def initialize 5 @items = [] # Initialize an empty cart 6 end 7 8 def item_count 9 @items.sum { |item| item[:quantity] } # Calculate the total item count 10 end 11 12 def total 13 @items.sum { |item| item[:price] * item[:quantity] } # Calculate the total price 14 end 15end 16 17# Example Usage 18cart = ShoppingCart.new 19puts cart.item_count # Output: 0 20puts cart.total # Output: 0
- Description: Verify that when a single item is added to the cart, the cart's item count increases, and the total price reflects the added item's price.
- Details:
- Use the
add_item(item)
method to add an item to the cart. - Confirm that the
item_count
method returns the correct number of items after an item is added. - Ensure the
total
method accurately calculates and returns the total price of items in the cart.
- Use the
- Examples: Adding an item
{'id' => '1', 'name' => 'Book', 'price' => 10}
should result in an item count of1
and a total cost of10
.
Ruby1class ShoppingCart 2 attr_reader :items 3 4 def initialize 5 @items = [] 6 end 7 8 def item_count 9 @items.sum { |item| item[:quantity] } 10 end 11 12 def total 13 @items.sum { |item| item[:price] * item[:quantity] } 14 end 15 16 def add_item(item) # New method to add a single item 17 @items << item.merge(quantity: 1) # Add item with a default quantity of 1 18 end 19end 20 21# Example Usage 22cart = ShoppingCart.new 23cart.add_item({ id: '1', name: 'Book', price: 10 }) # Add a single item to the cart 24puts cart.item_count # Output: 1 25puts cart.total # Output: 10
- Description: When multiple different items are added to the cart, the item count should reflect the total number of unique items, and the total price should be the sum of all individual item prices.
- Details:
- Accommodate adding different items using the
add_item()
method. - Ensure the
item_count
method accurately reflects the number of items added. - Verify that the
total
method correctly calculates the sum of all item prices.
- Accommodate adding different items using the
- Examples: Adding
{'id' => '1', 'name' => 'Book', 'price' => 10}
and{'id' => '2', 'name' => 'Pen', 'price' => 5}
should result in a count of2
and a total of15
.
Ruby1class ShoppingCart 2 attr_reader :items 3 4 def initialize 5 @items = [] 6 end 7 8 def item_count 9 @items.sum { |item| item[:quantity] } 10 end 11 12 def total 13 @items.sum { |item| item[:price] * item[:quantity] } 14 end 15 16 def add_item(item) # Existing method to add items 17 @items << item.merge(quantity: 1) # Add item with default quantity of 1 18 end 19end 20 21# Example Usage 22cart = ShoppingCart.new 23cart.add_item({ id: '1', name: 'Book', price: 10 }) # Add the first item 24cart.add_item({ id: '2', name: 'Pen', price: 5 }) # Add a second, different item 25puts cart.item_count # Output: 2 26puts cart.total # Output: 15
- Description: When multiple quantities of the same item are added to the cart, the item count should reflect the total quantity added, and the total price should be the item's unit price multiplied by the quantity.
- Details:
- Allow items to be added with a specified quantity using an
add_item(item, quantity)
method. - Ensure the
item_count
method returns the sum of all quantities added for an item. - Update the
total
method to calculate the total price using the unit price multiplied by the total quantity.
- Allow items to be added with a specified quantity using an
- Examples: Adding
{'id' => '1', 'name' => 'Book', 'price' => 10}
with a quantity of3
should have a count of3
and a total of30
.
Ruby1class ShoppingCart 2 attr_reader :items 3 4 def initialize 5 @items = [] 6 end 7 8 def item_count 9 @items.sum { |item| item[:quantity] } 10 end 11 12 def total 13 @items.sum { |item| item[:price] * item[:quantity] } 14 end 15 16 # Updated add_item method to handle multiple quantities 17 def add_item(item, quantity = 1) 18 existing_item = @items.find { |i| i[:id] == item[:id] } # Check if item already exists in the cart 19 if existing_item 20 existing_item[:quantity] += quantity # Update quantity if item exists 21 else 22 @items << item.merge(quantity: quantity) # Add new item with specified quantity 23 end 24 end 25end 26 27# Example Usage 28cart = ShoppingCart.new 29cart.add_item({ id: '1', name: 'Book', price: 10 }, 3) # Add item with a quantity of 3 30puts cart.item_count # Output: 3 31puts cart.total # Output: 30
- Description: When an item is removed from the cart, the item count should decrease accordingly, and the total price should adjust to reflect the removal.
- Details:
- Provide a
remove_item(id)
method to handle the removal of items from the cart. - Ensure the
item_count
method accurately reflects the new count after removal. - Update the
total
method to return the correct total price after an item has been removed.
- Provide a
- Examples: Adding
{'id' => '1', 'name' => 'Book', 'price' => 10}
in a quantity of2
and then removing one should result in a count of1
and a total of10
.
Ruby1class ShoppingCart 2 attr_reader :items 3 4 def initialize 5 @items = [] 6 end 7 8 def item_count 9 @items.sum { |item| item[:quantity] } 10 end 11 12 def total 13 @items.sum { |item| item[:price] * item[:quantity] } 14 end 15 16 def add_item(item, quantity = 1) 17 existing_item = @items.find { |i| i[:id] == item[:id] } 18 if existing_item 19 existing_item[:quantity] += quantity 20 else 21 @items << item.merge(quantity: quantity) 22 end 23 end 24 25 # New method to remove an item 26 def remove_item(id) 27 item = @items.find { |i| i[:id] == id } # Find the item by its ID 28 return unless item # Return if item doesn't exist 29 30 if item[:quantity] > 1 31 item[:quantity] -= 1 # Decrease quantity if more than 1 32 else 33 @items.delete(item) # Remove item completely if quantity is 1 34 end 35 end 36end 37 38# Example Usage 39cart = ShoppingCart.new 40cart.add_item({ id: '1', name: 'Book', price: 10 }, 2) # Add item with a quantity of 2 41cart.remove_item('1') # Remove one quantity of the item 42puts cart.item_count # Output: 1 43puts cart.total # Output: 10
Looking ahead to the practice exercises, you will have the opportunity to write tests and ensure they pass while practicing the Red-Green-Refactor cycle. Your implementation may differ from the provided solutions as we move forward, and that’s perfectly acceptable. Each practice session will begin from a solution foundation, allowing you to compare your approach with the guided solution and develop your features to ensure test success.
As you undertake these exercises, remember to engage in the Red-Green-Refactor cycle. Be sure to practice writing tests first and do not write implementation code unless the test asks for it.
Red! Green! Refactor!