Lesson 1
Practicing Test Driven Development with Ruby and RSpec
Introduction to Red-Green-Refactor Cycle

Welcome to your first lesson in this course dedicated to practicing Test Driven Development (TDD) using Ruby and RSpec. 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 objective 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.

The combination of these phases fosters a disciplined and iterative approach to development. By starting with a failing test, you solidify the requirement and focus on addressing only what is necessary to meet that specific need. The act of writing minimal code ensures that each part of the application is built to clear specifications, reducing the potential for errors. The refactoring phase allows you to clean up and reorganize code, ensuring it remains efficient and comprehensible, contributing to the overall robustness and quality of the software. Together, these phases provide a structured method to incrementally build, test, and polish code, ensuring high standards throughout the development process.

Requirements for `calculate_discount` Method

In this unit, we will outline the test cases that guide the implementation of the calculate_discount method by employing the Red-Green-Refactor cycle of Test Driven Development (TDD). Each test case serves as a specific requirement, starting with writing a failing test ("Red") to define a behavior, followed by implementing minimal code changes to pass the test ("Green"), and finalizing with code optimization ("Refactor"). We will cover essential scenarios, such as applying correct percentage-based discounts, handling zero discount cases, managing decimal discount precision, and ensuring valid input handling to guarantee robustness and correctness.

Implementation of `calculate_discount` Method

Here is the calculate_discount method that you will be working with:

Ruby
1def calculate_discount(original_price, discount_percentage) 2 raise ArgumentError, 'Price cannot be negative' if original_price < 0 3 raise ArgumentError, 'Discount percentage cannot be > 100%' if discount_percentage > 100 4 raise ArgumentError, 'Discount cannot be negative' if discount_percentage < 0 5 6 discounted_price = original_price * (1 - discount_percentage / 100.0) 7 discounted_price.round(2) 8end
1. Correct Discount Application
  • Description: The method must correctly apply a percentage-based discount to an original price.
  • Test Case:
    Ruby
    1require 'rspec' 2 3RSpec.describe 'calculate_discount' do 4 it 'applies the correct discount to the price' do 5 # Arrange 6 original_price = 100 7 discount_percentage = 20 8 expected_discounted_price = 80 9 10 # Act 11 result = calculate_discount(original_price, discount_percentage) 12 13 # Assert 14 expect(result).to eq(expected_discounted_price) 15 end 16end
2. Zero Discount Handling
  • Description: The method should return the original price when the discount percentage is 0.
  • Test Case:
    Ruby
    1require 'rspec' 2 3RSpec.describe 'calculate_discount' do 4 it 'returns the original price when discount is zero' do 5 # Arrange 6 original_price = 50 7 discount_percentage = 0 8 9 # Act 10 result = calculate_discount(original_price, discount_percentage) 11 12 # Assert 13 expect(result).to eq(original_price) 14 end 15end
3. Decimal Discount Precision
  • Description: The method must accurately handle decimal percentages in discount calculations and round the result to two decimal places.
  • Test Case:
    Ruby
    1require 'rspec' 2 3RSpec.describe 'calculate_discount' do 4 it 'handles decimal discounts correctly' do 5 # Arrange 6 original_price = 100 7 discount_percentage = 33.333 8 expected_discounted_price = 66.67 9 10 # Act 11 result = calculate_discount(original_price, discount_percentage) 12 13 # Assert 14 expect(result.round(2)).to eq(expected_discounted_price) 15 end 16end
4. Negative Price Handling
  • Description: The method should not accept negative prices and must throw an error if encountered.
  • Test Case:
    Ruby
    1require 'rspec' 2 3RSpec.describe 'calculate_discount' do 4 it 'throws an error for negative prices' do 5 # Arrange 6 original_price = -50 7 discount_percentage = 10 8 9 # Act, Assert 10 expect { calculate_discount(original_price, discount_percentage) }.to raise_error(ArgumentError, 'Price cannot be negative') 11 end 12end
5. Discount Percentage Greater Than 100%
  • Description: The method should not accept discount percentages greater than 100% and must throw an error in such cases.
  • Test Case:
    Ruby
    1require 'rspec' 2 3RSpec.describe 'calculate_discount' do 4 it 'throws an error for discounts greater than 100 percent' do 5 # Arrange 6 original_price = 100 7 discount_percentage = 110 8 9 # Act, Assert 10 expect { calculate_discount(original_price, discount_percentage) }.to raise_error(ArgumentError, 'Discount percentage cannot be > 100%') 11 end 12end
6. Negative Discount Percentage
  • Description: The method should not accept negative discount percentages and must throw an error in such cases.
  • Test Case:
    Ruby
    1require 'rspec' 2 3RSpec.describe 'calculate_discount' do 4 it 'throws an error for discounts less than zero' do 5 # Arrange 6 original_price = 100 7 discount_percentage = -10 8 9 # Act, Assert 10 expect { calculate_discount(original_price, discount_percentage) }.to raise_error(ArgumentError, 'Discount cannot be negative') 11 end 12end
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 provided 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.