Why TDD Is Not a Trendy Fad, But Your New Best Friend
\n\nImagine: you write code, everything works perfectly. You launch the application — no bugs. A week later you add a new feature, and... everything breaks. Sound familiar? If you've ever spent hours hunting for an error that appeared because of an accidental change in a neighboring module — this article is for you.
\n\nTest-Driven Development (TDD) is not just a methodology, but a real lifesaver for a developer. In the world of JavaScript, where code changes faster than the weather, TDD helps you stay afloat. Let's figure out how it works, and why after TDD you won't be able to write code the old way anymore.
\n\nWhat TDD Actually Is (Spoiler: It's Not About Tests)
\n\nMany people think TDD is simply "writing tests before code." But in reality, it's a design philosophy. TDD forces you to think about how your code will be used before you write it. It's like an architect who first draws a building plan and then starts construction.
\n\nThe TDD cycle consists of three steps (often called "Red-Green-Refactor"):
\n\n- \n
- Red: Write a test that is guaranteed to fail (because the functionality doesn't exist yet). \n
- Green: Write the minimal code to make the test pass. \n
- Refactor: Improve the code without changing its behavior (tests guarantee you haven't broken anything). \n
And yes, this cycle repeats for every small unit of functionality. Sounds like a slowdown? Possibly. But in the long run, you'll save hours of debugging.
\n\nWhy JavaScript Is Perfect for TDD
\n\nJavaScript is a dynamic language. This is both its strength and weakness. Runtime errors are common, especially in large projects. TDD transforms JavaScript from a "flexible but fragile" tool into a reliable platform.
\n\nMoreover, the JS ecosystem is rich in testing tools: Jest, Mocha, Chai, Jasmine. We'll use Jest — it's simple, powerful, and popular. If you're not familiar with Jest yet, don't worry: we'll start from the basics.
\n\nPractice: Building a Calculator with TDD
\n\nTheory is good, but let's move to real code. We'll write a simple calculator using TDD. This is a classic example, but it perfectly demonstrates the process.
\n\nStep 1: Project Setup
\n\nCreate a new folder and initialize the project:
\n\nmkdir tdd-calculator\ncd tdd-calculator\nnpm init -y\nnpm install --save-dev jest\n\n\nAdd a script to package.json:
"scripts": {\n "test": "jest"\n}\n\n\nStep 2: Red — Write a Test for Addition
\n\nCreate the file calculator.test.js:
// calculator.test.js\nconst { add } = require('./calculator');\n\ndescribe('Calculator', () => {\n test('adds 2 + 3 to equal 5', () => {\n expect(add(2, 3)).toBe(5);\n });\n});\n\n\nRun the test: npm test. It will fail with an error because the add function doesn't exist. This is normal — this is the red phase.
Step 3: Green — Write Minimal Code
\n\nCreate calculator.js:
// calculator.js\nfunction add(a, b) {\n return a + b;\n}\n\nmodule.exports = { add };\n\n\nRun the test again. Green? Great! We've just completed the green phase.
\n\nStep 4: Refactoring
\n\nThe code is simple, nothing to refactor. But imagine we want to add type checking. TDD allows you to do this safely: first the test, then the code.
\n\n// calculator.test.js\ntest('throws error if arguments are not numbers', () => {\n expect(() => add('2', 3)).toThrow('Arguments must be numbers');\n});\n\n\nNow modify add: