Lesson 1
Practice Red-Green-Refactor: Discount Calculation
Introduction to these practices

Welcome to your first lesson for this course dedicated to practicing Test Driven Development (TDD) utilizing TypeScript and Jest. Test Driven Development is an effective approach that prioritizes writing tests before coding. This guides you to develop your application around testing, ensuring that each component functions correctly as intended. In this lesson, you'll learn the fundamentals of TDD and the Red-Green-Refactor cycle and understand their roles in creating consistent and maintainable code.

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 implement code that makes each test pass, simulating a real-world TDD environment. As the course guide, it's akin to being your pair programmer, providing test-driven prompts to hone your skills as you progress.

Understanding the Red-Green-Refactor Cycle

As a reminder, the Red-Green-Refactor cycle is central to TDD, guiding your development process:

  • Red: Start by writing a failing test to define the next step.
  • Green: Implement just enough code to pass the test, focusing on functionality.
  • Refactor: Optimize the code for clarity and efficiency without changing its behavior.
Requirements for `calculateDiscount` Function
1. Correct Discount Application
  • Description: The function must correctly apply a percentage-based discount to an original price.
  • Test Case:
    TypeScript
    1it('should apply the correct discount to the price', () => { 2 // Arrange 3 const originalPrice = 100; 4 const discountPercentage = 20; 5 const expectedDiscountedPrice = 80; 6 7 // Act 8 const result = calculateDiscount(originalPrice, discountPercentage); 9 10 // Assert 11 expect(result).toBe(expectedDiscountedPrice); 12});
2. Zero Discount Handling
  • Description: The function should return the original price when the discount percentage is 0.
  • Test Case:
    TypeScript
    1it('should return the original price when discount is 0', () => { 2 // Arrange 3 const originalPrice = 50; 4 const discountPercentage = 0; 5 6 // Act 7 const result = calculateDiscount(originalPrice, discountPercentage); 8 9 // Assert 10 expect(result).toBe(originalPrice); 11});
3. Decimal Discount Precision
  • Description: The function must accurately handle decimal percentages in discount calculations and round the result to two decimal places.
  • Test Case:
    TypeScript
    1it('should handle decimal discounts correctly', () => { 2 // Arrange 3 const originalPrice = 100; 4 const discountPercentage = 33.333; 5 const expectedDiscountedPrice = 66.67; 6 7 // Act 8 const result = calculateDiscount(originalPrice, discountPercentage); 9 10 // Assert 11 expect(result).toBe(expectedDiscountedPrice); 12});
4. Negative Price Handling
  • Description: The function should not accept negative prices and must throw an error if encountered.
  • Test Case:
    TypeScript
    1it('should throw an error for negative prices', () => { 2 // Arrange 3 const originalPrice = -50; 4 const discountPercentage = 10; 5 6 // Act, Assert 7 expect(() => calculateDiscount(originalPrice, discountPercentage)).toThrow('Price cannot be negative'); 8});
5. Discount Percentage Greater Than 100%
  • Description: The function should not accept discount percentages greater than 100% and must throw an error in such cases.
  • Test Case:
    TypeScript
    1it('should throw an error for discounts greater than 100%', () => { 2 // Arrange 3 const originalPrice = 100; 4 const discountPercentage = 110; 5 6 // Act, Assert 7 expect(() => calculateDiscount(originalPrice, discountPercentage)).toThrow('Discount percentage cannot be > 100%'); 8});
6. Negative Discount Percentage
  • Description: The function should not accept negative discount percentages and must throw an error in such cases.
  • Test Case:
    TypeScript
    1it('should throw an error for discounts less than 0%', () => { 2 // Arrange 3 const originalPrice = 100; 4 const discountPercentage = -10; 5 6 // Act, Assert 7 expect(() => calculateDiscount(originalPrice, discountPercentage)).toThrow('Discount cannot be negative'); 8});
Summary and Preparation for Practice Exercises

Looking ahead to the practice exercises, you will have the opportunity to ensure all tests pass while practicing the Red-Green-Refactor cycle. Your implementation may differ from the provided solutions, 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. Each test I provide serves as the "Red" phase, marking the next step to achieve. Your task is to transition through the "Green" phase by making the tests pass, and then enter the "Refactor" phase to enhance your code's clarity and maintainability while confirming that all tests remain successful.

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.