JavaScript > Testing and Debugging > Unit Testing > Test-driven development (TDD)
TDD Example: Calculating Rectangle Area with Jest
This example demonstrates Test-Driven Development (TDD) using Jest. We'll write a simple function to calculate the area of a rectangle, following the Red-Green-Refactor cycle.
Introduction to TDD
Test-Driven Development (TDD) is a development process where you write tests *before* you write the code that implements the functionality. This forces you to think about the desired behavior of your code upfront. The core cycle of TDD is Red-Green-Refactor: 1. Red: Write a test that fails (because the code doesn't exist yet). 2. Green: Write the minimal amount of code to make the test pass. 3. Refactor: Improve the code while ensuring the tests still pass.
Setting up Jest
First, you'll need to install Jest. Open your terminal and navigate to your project directory. Run the following command to install Jest as a development dependency. After installation, update your `package.json` file to include a test script. "scripts": { "test": "jest" }
npm install --save-dev jest
Writing the First Test (Red)
Create a file named `rectangle.test.js`. This file will contain our tests for the `calculateArea` function. We're starting with a simple test case: checking if the function correctly calculates the area of a rectangle with width 5 and height 10. We also add tests for negative and zero inputs. At this point, running `npm test` will fail, because the `calculateArea` function doesn't exist.
// rectangle.test.js
describe('calculateArea', () => {
test('should calculate the area of a rectangle with width 5 and height 10', () => {
expect(calculateArea(5, 10)).toBe(50);
});
test('should return 0 if either width or height is negative', () => {
expect(calculateArea(-5, 10)).toBe(0);
expect(calculateArea(5, -10)).toBe(0);
expect(calculateArea(-5, -10)).toBe(0);
});
test('should handle zero values correctly', () => {
expect(calculateArea(0, 10)).toBe(0);
expect(calculateArea(5, 0)).toBe(0);
expect(calculateArea(0, 0)).toBe(0);
});
});
Implementing the Function (Green)
Now, create a file named `rectangle.js` and implement the `calculateArea` function. This implementation provides the minimum code required to pass the test we wrote earlier. It includes a check for negative or zero dimensions, returning 0 in those cases. Now, after running `npm test`, all tests should pass.
// rectangle.js
function calculateArea(width, height) {
if (width <= 0 || height <= 0) {
return 0;
}
return width * height;
}
module.exports = calculateArea;
Refactoring (Refactor)
While the code works, we can refactor it to be more concise. In this case, we can simplify the if statement. Run `npm test` again to ensure the refactoring didn't break anything. The tests should still pass.
// rectangle.js (Refactored)
function calculateArea(width, height) {
if (width <= 0 || height <= 0) return 0; // Concise check
return width * height;
}
module.exports = calculateArea;
Concepts behind the snippet
This example highlights the core principles of TDD: writing tests before code, focusing on a single unit of functionality at a time, and iterating through the Red-Green-Refactor cycle.
Real-Life Use Case Section
Imagine you are building an e-commerce platform and need to calculate the total cost of items in a shopping cart. Using TDD, you'd first write tests to ensure the calculation handles various scenarios correctly: discounts, taxes, shipping fees, empty carts, etc. This proactive testing approach helps catch potential errors early, leading to a more robust and reliable shopping cart functionality.
Best Practices
Interview Tip
When discussing TDD in an interview, emphasize the benefits of proactive testing, improved code design, and reduced debugging time. Be prepared to explain the Red-Green-Refactor cycle with a concrete example, like the one provided above.
When to use them
TDD is highly effective when dealing with complex logic, critical functionalities, or when you want to ensure high code quality and prevent regressions. It's also beneficial in collaborative environments where clear specifications and testable code are essential.
Memory footprint
TDD itself doesn't directly impact memory footprint. However, well-tested code is more likely to be optimized and less prone to memory leaks or inefficient resource usage. The testing framework used (e.g., Jest) does have its own memory footprint, which should be considered, especially in large projects. However, the benefits of TDD generally outweigh the overhead.
Alternatives
Pros
Cons
FAQ
-
What happens if a test fails after refactoring?
If a test fails after refactoring, it indicates that the refactoring introduced a bug or changed the behavior of the code in an unexpected way. You should then analyze the changes made during refactoring, identify the source of the problem, and correct the code. The failing test serves as a valuable signal that something went wrong. -
Is TDD suitable for all types of projects?
While TDD can be beneficial in many projects, it may not be the best fit for every situation. It is most effective for projects with well-defined requirements, complex logic, or when high code quality is critical. For small, simple projects or prototypes, the overhead of TDD might not be justified. However, even in those cases, writing some tests can still improve code quality.