Welcome to your fifth and final unit of this course, dedicated to practicing Test-Driven Development (TDD) utilizing Ruby and RSpec. We're going to finish building our ShoppingCart
system by adding even more features to our class.
In this course, emphasis is placed on hands-on practice, where you'll receive requirements through tests, one at a time. Your task is to write tests AND implement code that makes each test pass, simulating a real-world TDD environment. Previously, I wrote the tests for you; this time, it's all up to you!
Remember to use the core concepts of the Red-Green-Refactor cycle while completing these coding exercises. I'm still here to help! Just ask.
In this section, you'll learn how to implement the following features:
- Enforcing a Quantity Limit for a Single Item
- Retrieving Item Details by ID
- Applying and Validating Discount Codes
- Handling the Addition of Existing Items and Ensuring Quantity Limits
- Description: The cart should enforce a maximum quantity limit of
10
for a single type of item, preventing more than the allowed amount from being added. - Details
- Utilize the
add_item(item, quantity)
method to add items to the cart. - Ensure an exception is raised when adding a quantity that exceeds a limit of
10
for a single item. - The exception message should clearly state, "Maximum quantity exceeded" when the limit is breached.
- Utilize the
- Examples: Attempting to add
11
units ofProduct.new('1', 'Book', 10)
should raise an exception indicating "Maximum quantity exceeded."
Ruby1class ShoppingCart 2 attr_reader :items 3 4 def initialize 5 @items = [] 6 end 7 8 def add_item(item, quantity = 1) 9 existing_item = @items.find { |i| i[:id] == item[:id] } 10 new_quantity = existing_item ? existing_item[:quantity] + quantity : quantity 11 raise "Maximum quantity exceeded" if new_quantity > 10 12 13 if existing_item 14 existing_item[:quantity] = new_quantity 15 else 16 @items << item.merge(quantity: quantity) 17 end 18 end 19end 20 21# Example Usage 22cart = ShoppingCart.new 23begin 24 cart.add_item({ id: '1', name: 'Book', price: 10 }, 11) 25rescue => e 26 puts e.message # Output: "Maximum quantity exceeded" 27end
- Description: When an item is added to the cart, it should be possible to retrieve the item details using its ID, which includes the product information and its quantity in the cart.
- Details
- Enable items to be added using an
add_item()
method with specific IDs. - Ensure
get_item(id)
returns the correct item details, including the quantity after being added to the cart.
- Enable items to be added using an
- Examples: Adding an item with
Product.new('1', 'Book', 10)
and retrieving it by ID1
should return{'id' => '1', 'name' => 'Book', 'price' => 10, 'quantity' => 1}
.
Ruby1class ShoppingCart 2 attr_reader :items 3 4 def initialize 5 @items = [] 6 end 7 8 def add_item(item, quantity = 1) 9 existing_item = @items.find { |i| i[:id] == item[:id] } 10 if existing_item 11 existing_item[:quantity] += quantity 12 else 13 @items << item.merge(quantity: quantity) 14 end 15 end 16 17 def get_item(id) 18 item = @items.find { |i| i[:id] == id } 19 item_details = item&.slice(:id, :name, :price, :quantity) 20 item_details 21 end 22end 23 24# Example Usage 25cart = ShoppingCart.new 26cart.add_item({ id: '1', name: 'Book', price: 10 }, 1) 27puts cart.get_item('1') # Output: {"id"=>"1", "name"=>"Book", "price"=>10, "quantity"=>1}
- Description: Applying a valid discount code should reduce the total price of items in the cart by the specified discount percentage.
- Details
- Use the
apply_discount_code(code)
method to apply a discount. - Support valid discount codes like
'HOLIDAY25'
for a25%
discount. - Update
get_total()
to reflect the discounted price.
- Use the
- Examples: Applying the discount code
'HOLIDAY25'
to an item{'id' => '1', 'name' => 'Book', 'price' => 100}
should reduce the total to75
.
Ruby1class ShoppingCart 2 attr_reader :items 3 4 def initialize 5 @items = [] 6 end 7 8 def add_item(item, quantity = 1) 9 existing_item = @items.find { |i| i[:id] == item[:id] } 10 if existing_item 11 existing_item[:quantity] += quantity 12 else 13 @items << item.merge(quantity: quantity) 14 end 15 end 16 17 def get_item(id) 18 item = @items.find { |i| i[:id] == id } 19 item_details = item&.slice(:id, :name, :price, :quantity) 20 item_details 21 end 22end 23 24# Example Usage 25cart = ShoppingCart.new 26cart.add_item({ id: '1', name: 'Book', price: 10 }, 1) 27puts cart.get_item('1') # Output: {"id"=>"1", "name"=>"Book", "price"=>10, "quantity"=>1}
- Description: The system should not accept discount codes that are invalid and should raise an appropriate exception when such a code is applied.
- Details
- Ensure
apply_discount_code(code)
checks against a list of valid codes. - Raise an exception with the message "Invalid discount code" if the code is not valid.
- Ensure
- Examples: Applying the discount code
'INVALID'
after adding an item should raise an exception indicating the code is invalid.
Ruby1class ShoppingCart 2 attr_reader :items 3 4 def initialize 5 @items = [] 6 @discount = 0 7 end 8 9 def add_item(item, quantity = 1) 10 existing_item = @items.find { |i| i[:id] == item[:id] } 11 if existing_item 12 existing_item[:quantity] += quantity 13 else 14 @items << item.merge(quantity: quantity) 15 end 16 end 17 18 def apply_discount_code(code) 19 valid_codes = { 'HOLIDAY25' => 0.25 } 20 @discount = valid_codes[code] || (raise "Invalid discount code") 21 end 22 23 def total 24 subtotal = @items.sum { |item| item[:price] * item[:quantity] } 25 discount_amount = subtotal * @discount 26 subtotal - discount_amount 27 end 28end 29 30# Example Usage 31cart = ShoppingCart.new 32cart.add_item({ id: '1', name: 'Book', price: 100 }, 1) 33begin 34 cart.apply_discount_code('INVALID') 35rescue => e 36 puts e.message # Output: "Invalid discount code" 37end
- Description: When an item that already exists in the cart is added again, its quantity should increase without duplicates, and the total price should reflect the cumulative price.
- Details
- Allow items to be added again using the
add_item()
method without creating duplicates in the cart. - Ensure
get_item(id)
returns the updated quantity after adding the same item. - Update the total price to reflect the price of the added items' cumulative quantities.
- Allow items to be added again using the
- Examples: Adding
Product.new('1', 'Book', 200)
twice should result in a quantity of2
for that item, with the total updated price reflecting the double addition.
Ruby1class ShoppingCart 2 attr_reader :items 3 4 def initialize 5 @items = [] 6 end 7 8 def add_item(item, quantity = 1) 9 existing_item = @items.find { |i| i[:id] == item[:id] } 10 if existing_item 11 existing_item[:quantity] += quantity 12 else 13 @items << item.merge(quantity: quantity) 14 end 15 end 16 17 def get_item(id) 18 item = @items.find { |i| i[:id] == id } 19 item_details = item&.slice(:id, :name, :price, :quantity) 20 item_details 21 end 22 23 def total 24 @items.sum { |item| item[:price] * item[:quantity] } 25 end 26end 27 28# Example Usage 29cart = ShoppingCart.new 30cart.add_item({ id: '1', name: 'Book', price: 200 }, 1) 31cart.add_item({ id: '1', name: 'Book', price: 200 }, 1) 32puts cart.get_item('1') # Output: {"id"=>"1", "name"=>"Book", "price"=>200, "quantity"=>2} 33puts cart.total # Output: 400
- Description: When an existing item in the cart has more units added to it, the total quantity should not exceed the predefined maximum limit.
- Details
- Utilize the
add_item(item)
method to add to an existing item. - Ensure that adding a quantity that results in exceeding the maximum allowed quantity raises an exception.
- The exception message should be "Maximum quantity exceeded" when the quantity limit is breached.
- Utilize the
- Example: Adding
3
units ofProduct.new('1', 'Book', 200)
to an existing8
units should raise an exception, as it exceeds the limit.
Ruby1class ShoppingCart 2 attr_reader :items 3 4 def initialize 5 @items = [] 6 end 7 8 def add_item(item, quantity = 1) 9 existing_item = @items.find { |i| i[:id] == item[:id] } 10 if existing_item 11 new_quantity = existing_item[:quantity] + quantity 12 raise "Maximum quantity exceeded" if new_quantity > 10 13 existing_item[:quantity] = new_quantity 14 else 15 raise "Maximum quantity exceeded" if quantity > 10 16 @items << item.merge(quantity: quantity) 17 end 18 end 19 20 def total 21 @items.sum { |item| item[:price] * item[:quantity] } 22 end 23end 24 25# Example Usage 26cart = ShoppingCart.new 27cart.add_item({ id: '1', name: 'Book', price: 200 }, 8) 28begin 29 cart.add_item({ id: '1', name: 'Book', price: 200 }, 3) 30rescue => e 31 puts e.message # Output: "Maximum quantity exceeded" 32end
In this unit, you reviewed descriptions for more advanced test cases to expand the functionality of the ShoppingCart
class, covering features like handling non-existent items, applying discounts, and updating item quantities. With your solidified TDD skills, let's proceed to writing targeted test cases that capture essential functionality for a robust ShoppingCart
class.
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.
And you're almost done with this practice course! Great work! You're almost there.
Red! Green! Refactor!