Introduction to the Shopping Cart Module

Welcome to your third unit for this course dedicated to practicing Test Driven Development (TDD) utilizing Swift and XCTest. We're going to start building a new system; this time, we'll create a ShoppingCart class with multiple features.

This course emphasizes 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, tests were provided for you, but this time, it's all up to you! Start by asserting what the output should be, even if the method or property doesn’t exist yet. This mimics real-world workflows, where features are often discussed and validated via tests before any implementation begins. Think in terms of: What do I expect this function to return or do?

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 if you encounter issues.

Requirements for `ShoppingCart` Class
1. Starting with an Empty Cart
  • 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 getItemCount() returns 0 for an empty cart.
    • Verify getTotal() returns 0 for the initial state.
  • Examples: A newly created cart should have a count of 0 and a total of 0.
Swift
1import XCTest 2 3class ShoppingCart { 4 private var items: [String: (name: String, price: Double, quantity: Int)] = [:] 5 6 func getItemCount() -> Int { 7 return items.values.reduce(0) { $0 + $1.quantity } 8 } 9 10 func getTotal() -> Double { 11 return items.values.reduce(0.0) { $0 + ($1.price * Double($1.quantity)) } 12 } 13} 14 15class ShoppingCartTests: XCTestCase { 16 func testEmptyCart() { 17 let cart = ShoppingCart() 18 XCTAssertEqual(cart.getItemCount(), 0) 19 XCTAssertEqual(cart.getTotal(), 0.0) 20 } 21}
2. Adding a Single Item
  • 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 addItem(item:) method to add an item to the cart.
    • Confirm that getItemCount() returns the correct number of items after an item is added.
    • Ensure getTotal() accurately calculates and returns the total price of items in the cart.
  • Examples: Adding an item ("1", "Book", 10) should result in an item count of 1 and a total cost of 10.
Swift
1extension ShoppingCart { 2 func addItem(id: String, name: String, price: Double, quantity: Int = 1) { 3 if let existingItem = items[id] { 4 items[id] = (name, price, existingItem.quantity + quantity) 5 } else { 6 items[id] = (name, price, quantity) 7 } 8 } 9} 10 11extension ShoppingCartTests { 12 func testAddSingleItem() { 13 let cart = ShoppingCart() 14 cart.addItem(id: "1", name: "Book", price: 10.0) 15 XCTAssertEqual(cart.getItemCount(), 1) 16 XCTAssertEqual(cart.getTotal(), 10.0) 17 } 18}
3. Adding Multiple Items
  • 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 addItem() method.
    • Ensure getItemCount() accurately reflects the number of items added.
    • Verify that getTotal() correctly calculates the sum of all item prices.
  • Examples: Adding ("1", "Book", 10) and ("2", "Pen", 5) should result in a count of 2 and a total of 15.
Swift
1extension ShoppingCartTests { 2 func testAddMultipleItems() { 3 let cart = ShoppingCart() 4 cart.addItem(id: "1", name: "Book", price: 10.0) 5 cart.addItem(id: "2", name: "Pen", price: 5.0) 6 XCTAssertEqual(cart.getItemCount(), 2) 7 XCTAssertEqual(cart.getTotal(), 15.0) 8 } 9}
4. Handling Multiple Quantities of the Same Item
  • 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 addItem(id:name:price:quantity:) method.
    • Ensure getItemCount() returns the sum of all quantities added for an item.
    • Update getTotal() to calculate the total price using the unit price multiplied by the total quantity.
  • Examples: Adding ("1", "Book", 10) with a quantity of 3 should have a count of 3 and a total of 30.
Swift
1extension ShoppingCartTests { 2 func testAddMultipleQuantitiesOfSameItem() { 3 let cart = ShoppingCart() 4 cart.addItem(id: "1", name: "Book", price: 10.0, quantity: 3) 5 XCTAssertEqual(cart.getItemCount(), 3) 6 XCTAssertEqual(cart.getTotal(), 30.0) 7 } 8}

This test ensures that adding multiple quantities at once behaves the same as adding the item multiple times across calls. For example, calling addItem() with quantity 3 should be functionally identical to calling it three times with quantity 1. This validates that the internal logic accumulates item quantities correctly rather than overwriting them.

5. Removing an Item
  • 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 removeItem(id:) method to handle the removal of items from the cart.
    • Ensure getItemCount() accurately reflects the new count after removal.
    • Update getTotal() to return the correct total price after an item has been removed.
  • Examples: Adding ("1", "Book", 10) in a quantity of 2 and then removing one should result in a count of 1 and a total of 10.
Swift
1extension ShoppingCart { 2 func removeItem(id: String) { 3 items.removeValue(forKey: id) 4 } 5} 6 7extension ShoppingCartTests { 8 func testRemoveItem() { 9 let cart = ShoppingCart() 10 cart.addItem(id: "1", name: "Book", price: 10.0, quantity: 2) 11 cart.removeItem(id: "1") 12 XCTAssertEqual(cart.getItemCount(), 0) 13 XCTAssertEqual(cart.getTotal(), 0.0) 14 } 15}

The current implementation of removeItem(id:) removes the item entirely, regardless of quantity. If you wanted to support decrementing one unit at a time, you’d need to modify this logic to reduce the quantity and only remove the item when quantity reaches zero. For now, the full removal simplifies logic and matches the test expectations.

Summary and Preparation for Practice Exercises

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!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal