Building a Shopping Cart with Many Requirements through TDD

Introduction to the Shopping Cart Module

Welcome to the third unit of this course dedicated to practicing Test Driven Development (TDD) utilizing Rust. In this module, we will focus on creating a shopping cart with a variety of features. Throughout 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. Unlike previous exercises, where tests were supplied, this time, you're in charge!

Remember to utilize the core concepts of the Red-Green-Refactor cycle while working through these exercises. Assistance is available, so don't hesitate to ask for help.

1. Starting with an Empty Cart

  • Description: When a new ShoppingCart is created, it should start without any products, and the total price should be zero.

  • Details:

    • Define a new struct ShoppingCart in Rust.
    • Implement an impl block with methods get_item_count() returning an integer and get_total() returning a float for the ShoppingCart struct.
    • Ensure the function get_item_count() returns 0 for an empty cart.
    • Verify the function get_total() returns 0.0 for the initial state.
  • Examples:

    pub struct ShoppingCart {
        // Define necessary fields
    }
    
    impl ShoppingCart {
        pub fn new() -> Self {
            // Initialize a new ShoppingCart
        }
        
        pub fn get_item_count(&self) -> usize {
            // Return the count of cart items
        }
        
        pub fn get_total(&self) -> f64 {
            // Return the total price
        }
    }
    #[cfg(test)]
    mod tests {
        use project::*;
    
        #[test]
        fn test_empty_cart() {
            let cart = ShoppingCart::new();
            assert_eq!(cart.get_item_count(), 0);
            assert_eq!(cart.get_total(), 0.0);
        }
    }

2. Adding a Single Product

  • Description: Verify that adding a single product to the cart increases the item count and adjusts the total price to include the price of the added product.

  • Details:

    • Define a struct Product and implement the add_item() method in the impl block for ShoppingCart.
    • Confirm that get_item_count() reflects the change in the number of items.
    • Ensure get_total() accurately accounts for the total price of products in the cart.
  • Examples:

    pub struct Product {
        pub id: String,
        pub name: String,
        pub price: f64,
    }
    
    impl ShoppingCart {
        pub fn add_item(&mut self, product: Product) {
            // Add the product to the cart
        }
    }
    #[cfg(test)]
    mod tests {
        use project::*;
    
        #[test]
        fn test_adding_single_product() {
            let mut cart = ShoppingCart::new();
            let book = Product {
                id: "1".to_string(),
                name: "Book".to_string(),
                price: 10.0,
            };
            cart.add_item(book);
            assert_eq!(cart.get_item_count(), 1);
            assert_eq!(cart.get_total(), 10.0);
        }
    }

3. Adding Multiple Products

  • Description: Adding multiple distinct products to the shopping cart should update the item count to reflect the total number of unique products, and the total price should equal the sum of the prices of all products added.

  • Details:

    • Utilize a Vec<Product> to store products in the ShoppingCart and implement the handling of multiple products through add_item().
    • Confirm that get_item_count() correctly represents the total number of unique products added to the cart.
    • Verify that get_total() accurately calculates the sum of the prices for all individual products added.
  • Examples:

    #[cfg(test)]
    mod tests {
        use project::*;
    
        #[test]
        fn test_adding_multiple_products() {
            let mut cart = ShoppingCart::new();
            let book = Product {
                id: "1".to_string(),
                name: "Book".to_string(),
                price: 10.0,
            };
            let pen = Product {
                id: "2".to_string(),
                name: "Pen".to_string(),
                price: 5.0,
            };
            cart.add_item(book);
            cart.add_item(pen);
            assert_eq!(cart.get_item_count(), 2);
            assert_eq!(cart.get_total(), 15.0);
        }
    }

4. Handling Multiple Quantities of the Same Product

  • Description: Adding multiple quantities of the same product should adjust the item count to reflect the total quantity and set the total price to be the product of the unit price and the quantity.

  • Details:

    • Evolve the add_item() method to handle adding a product with a specified quantity.
    • Ensure that get_item_count() shows the sum of all product quantities.
    • Confirm that get_total() accurately calculates the total price by multiplying the unit price by the total quantity.
  • Examples:

    impl ShoppingCart {
        pub fn add_item(&mut self, product: Product, quantity: usize) {
            // Add product with specified quantity
        }
    }
    #[cfg(test)]
    mod tests {
        use project::*;
    
        #[test]
        fn test_adding_multiple_quantities_of_same_product() {
            let mut cart = ShoppingCart::new();
            let book = Product {
                id: "1".to_string(),
                name: "Book".to_string(),
                price: 10.0,
            };
            cart.add_item(book, 3);
            assert_eq!(cart.get_item_count(), 3);
            assert_eq!(cart.get_total(), 30.0);
        }
    }

5. Removing a Product

  • Description: Removing a product from the cart should result in a decreased item count and an updated total price.

  • Details:

    • Implement a remove_item(id: &str) method to remove products from the cart by their ID.
    • Ensure that get_item_count() correctly reflects the number of items after removal.
    • Modify get_total() to return the updated total price after the product is removed.
  • Examples:

    impl ShoppingCart {
        pub fn remove_item(&mut self, id: &str) {
            // Remove the product from the cart by id
        }
    }
    #[cfg(test)]
    mod tests {
        use project::*;
    
        #[test]
        fn test_removing_product() {
            let mut cart = ShoppingCart::new();
            let book = Product {
                id: "1".to_string(),
                name: "Book".to_string(),
                price: 10.0,
            };
            cart.add_item(book, 2);
            cart.remove_item("1");
            assert_eq!(cart.get_item_count(), 1);
            assert_eq!(cart.get_total(), 10.0);
        }
    }

Summary and Preparation for Practice Exercises

Looking ahead, you'll engage in practice sessions where you'll write tests and ensure their successful execution while employing the Red-Green-Refactor cycle. Your implementations may differ from expected solutions, and that's perfectly fine. Each session starts from a basic foundation, offering you the opportunity to compare approaches and refine your skills in ensuring test success.

As you progress through these exercises, remain focused on the Red-Green-Refactor principles. Begin by writing tests and execute only those implementation steps that the tests request.

Red! Green! Refactor!

Make sure to include clear and concise tests for each requirement using Rust’s built-in testing framework to validate that your implementations are functioning as expected.

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