Introduction: Why Your Code Deserves Respect (and Tests)
\n\nImagine: you write code, it works, everything is fine. But a month later you return to the project, add a new feature — and suddenly everything breaks. Sound familiar? If so, then TDD in PHP is what you need. Test-Driven Development is not just a trendy buzzword, but a philosophy that changes your attitude towards code. In this article, we'll break down how TDD turns chaos into order and bugs into artifacts of the past. You'll learn how to write PHP tests that actually save time, and see it with code examples. Let's go!
\n\nWhat is TDD and Why Do You Need It in PHP?
\n\nTDD in PHP is a methodology where you first write a test, and then the code that passes that test. Sounds strange? Actually, it's brilliant. Instead of guessing whether your code works, you get instant feedback. The main principles of TDD:
\n\n- \n
- Red — write a test that fails (because the code isn't written yet). \n
- Green — write the minimum code to make the test pass. \n
- Refactor — improve the code without breaking the tests. \n
This cycle repeats over and over. In PHP, TDD is especially useful because the language is often used for web applications, where errors can be costly. Tests with PHPUnit (the main testing framework) help avoid regressions and make code transparent. Plus, TDD instills discipline: you start thinking about design before writing code.
\n\nSetting Up the Environment for PHP Testing
\n\nBefore diving into examples, let's prepare the environment. For test-driven development in PHP, we'll need PHPUnit. Installation is simple:
\n\ncomposer require --dev phpunit/phpunit ^10\n\nCreate a tests folder in the project root. Inside, create a file CalculatorTest.php. Make sure you have PHP 8.0+ installed. Now everything is ready for the first test.
First Test: Writing a Calculator with TDD
\n\nLet's create a simple calculator. First — the test. This is a classic example of TDD PHP for beginners.
\n\nStep 1: Red (write a failing test)
\n\nCreate tests/CalculatorTest.php:
<?php\n\nuse PHPUnit\\Framework\\TestCase;\n\nclass CalculatorTest extends TestCase\n{\n public function testAddition()\n {\n $calculator = new Calculator();\n $result = $calculator->add(2, 3);\n $this->assertEquals(5, $result);\n }\n}\n\nRun the test:
\n\n./vendor/bin/phpunit tests/CalculatorTest.php\n\nExpect an error: Class "Calculator" not found. All according to plan — this is the red phase.
Step 2: Green (write minimal code)
\n\nCreate src/Calculator.php:
<?php\n\nclass Calculator\n{\n public function add(int $a, int $b): int\n {\n return $a + $b;\n }\n}\n\nDon't forget to include the autoloader in the test:
\n\nrequire_once __DIR__ . '/../src/Calculator.php';\n\nRun the test again — green! Now refactoring. At this stage the code is simple, but later you can improve it without fear of breaking anything.
\n\nStep 3: Refactor with Confidence
\n\nSuppose we want to add type checking. Change the code:
\n\npublic function add(int $a, int $b): int\n{\n if (!is_int($a) || !is_int($b)) {\n throw new InvalidArgumentException('Only integers allowed');\n }\n return $a + $b;\n}\n\nRun the tests — they still pass. TDD gives you the courage to experiment.
\n\nAdvanced TDD: Working with Exceptions and Mocks
\n\nOnce you master the basics, move on to complex scenarios. For example, testing exceptions. In PHP unit tests, this is