Welcome to your second lesson in this course dedicated to practicing Test Driven Development (TDD) utilizing Ruby and RSpec. In this unit, we will continue adding features to our calculate_discount
function. We have five more requirements for you to implement!
In this course, the 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.
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.
In this section, you will enhance the calculate_discount
function by implementing five additional requirements:
-
Non-Numeric Price Input Handling: Learn how to validate inputs and handle errors for non-numeric price values.
-
Non-Numeric Discount Input Handling: Understand how to ensure discount inputs are numeric and manage invalid cases effectively.
-
Handling Very Small Prices: Discover techniques to manage very small prices and ensure they adhere to a defined minimum value post-discount.
-
Minimum Discount Percentage Application: Implement logic to apply a minimum discount percentage when provided with a lower percentage.
-
Capping Maximum Discount Amount: Explore methods for capping the discount at a maximum dollar value, ensuring the discount does not exceed this cap.
Below is the calculate_discount
function that you will enhance by implementing five additional requirements:
Ruby1def calculate_discount(original_price, discount_percentage) 2 # Placeholder for initial implementation 3 # This version assumes valid numeric inputs and just calculates the discount 4 original_price - (original_price * (discount_percentage / 100.0)) 5end
- Description: The function should validate that the price is a numeric value and raise an error if a non-numeric input is provided.
- Test Case:
Ruby
1describe '#calculate_discount' do 2 it 'raises an error for non-numeric price inputs' do 3 original_price = 'invalid' 4 discount_percentage = 10 5 6 expect { 7 calculate_discount(original_price, discount_percentage) 8 }.to raise_error(TypeError, 'Price must be a valid number') 9 end 10end
- Description: The function should validate that the discount percentage is a numeric value and raise an error if a non-numeric input is provided.
- Test Case:
Ruby
1describe '#calculate_discount' do 2 it 'raises an error for non-numeric discount inputs' do 3 original_price = 100 4 discount_percentage = 'invalid' 5 6 expect { 7 calculate_discount(original_price, discount_percentage) 8 }.to raise_error(TypeError, 'Discount must be a valid number') 9 end 10end
- Description: The function should ensure that very small prices do not fall below a defined minimum value after discounts are applied.
- Test Case:
Ruby
1describe '#calculate_discount' do 2 it 'handles very small prices correctly' do 3 original_price = 0.001 4 discount_percentage = 1 5 expected_discounted_price = 0.01 6 7 result = calculate_discount(original_price, discount_percentage) 8 9 expect(result).to eq(expected_discounted_price) 10 end 11end
- Description: The function should apply a minimum discount percentage of 1% if a lower percentage is provided.
- Test Case:
Ruby
1describe '#calculate_discount' do 2 it 'applies a minimum 1% discount when lower is provided' do 3 original_price = 100 4 discount_percentage = 0.1 5 expected_discounted_price = 99 6 7 result = calculate_discount(original_price, discount_percentage) 8 9 expect(result).to eq(expected_discounted_price) 10 end 11end
- Description: The function should cap the discount at a maximum dollar value of $500, even if the calculated discount would exceed that cap.
- Test Case:
Ruby
1describe '#calculate_discount' do 2 it 'caps the maximum discount amount at $500' do 3 original_price = 2500 4 discount_percentage = 30 5 expected_discounted_price = 2000 6 7 result = calculate_discount(original_price, discount_percentage) 8 9 expect(result).to eq(expected_discounted_price) 10 end 11end
Here is the complete calculate_discount
function after implementing all the requirements:
Ruby1def calculate_discount(original_price, discount_percentage) 2 raise TypeError, 'Price must be a valid number' unless original_price.is_a?(Numeric) 3 raise TypeError, 'Discount must be a valid number' unless discount_percentage.is_a?(Numeric) 4 5 discount_percentage = [discount_percentage, 1].max 6 discount_amount = original_price * (discount_percentage / 100.0) 7 discount_amount = [discount_amount, 500].min 8 9 discounted_price = original_price - discount_amount 10 [discounted_price, 0.01].max 11end
In this section, you explored testing advanced requirements for the calculate_discount
function. These included handling invalid, non-numeric inputs; managing very small prices to ensure minimum price thresholds are met; and applying specific discount constraints, such as enforcing a minimum discount percentage and capping the maximum allowable discount amount.
As you proceed with the practice exercises, keep the Red-Green-Refactor cycle in mind to guide your process. In this unit:
- Each test prompts you to refine the function's robustness, beginning with the Red phase by writing tests for additional edge cases.
- In the Green phase, aim to pass each test by incrementally adding necessary logic, such as input validation and boundary enforcement.
- The Refactor phase offers the chance to simplify and optimize your code after passing all tests, ensuring that it is not only correct but also clear and maintainable.
These exercises deepen your TDD skills by applying practical constraints and validations, simulating real-world requirements where robustness and reliability are essential.