← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

Test-Driven Development (TDD)

testing PHP 7.0+ Intermediate

Also Known As

TDD test-driven development red green refactor test-first PHPUnit TDD

TL;DR

A development practice where tests are written before the code they test — Red (write a failing test), Green (write the minimum code to pass), Refactor (improve without breaking tests).

Explanation

TDD follows a three-step cycle: Red — write a test for the desired behaviour that fails because the implementation does not exist yet; Green — write the minimum code needed to make the test pass (no more); Refactor — improve the implementation while keeping all tests green. This cycle runs in minutes, not hours. TDD benefits: forces design thinking before coding (if a class is hard to test, it is poorly designed); produces a complete test suite as a byproduct; enables confident refactoring; provides executable documentation of expected behaviour. In PHP, PHPUnit is the standard test framework; Pest provides a more expressive syntax. TDD does not mean writing every line of code test-first — it means starting with tests for behaviour, especially for business logic, edge cases, and error conditions. UI and integration tests are often written after because they are slower and harder to write first.

Common Misconception

TDD means writing all tests before any production code. TDD is a micro-cycle applied to individual features, not a requirement to plan the entire test suite before touching the codebase. Write a test for the next small piece of behaviour you want to add, make it pass, then move on. It is also fine to write tests after for legacy code you are modifying — the practice scales from strict TDD to test-augmented development depending on context.

Why It Matters

TDD produces better-designed code because writing a test first forces you to think about how the class will be used before you implement it. A class that is hard to construct in a test is hard to use in practice — TDD surfaces this before the design is locked in. The test suite produced by TDD also enables safe refactoring: you can reorganise implementation details confidently knowing that failing tests immediately signal any broken behaviour.

Common Mistakes

  • Writing tests after all the code is complete — this is testing, not TDD; the design benefits only come from writing tests first.
  • Writing tests that test implementation details rather than behaviour — test what the method does, not how it does it.
  • Skipping the Refactor step — Green does not mean done; code written to pass tests fast is often messy and must be cleaned up.
  • Not running tests in under one second — if the test suite is slow, developers stop running it; keep unit tests fast by avoiding database and HTTP calls.

Avoid When

  • Exploratory or spike code where the design is unknown — write tests after the spike, before productionising.
  • Simple getter/setter or pure configuration code where tests add noise without catching meaningful bugs.
  • Legacy code with no test infrastructure — retrofitting TDD into an untestable codebase requires refactoring first.
  • Deadline pressure that makes writing tests first slower than the value it provides in that specific context.

When To Use

  • Complex business logic with many edge cases — tests drive out the cases you would otherwise miss.
  • APIs and interfaces that must be stable — writing the test first defines the contract before implementation.
  • Bug fixes — write a failing test that reproduces the bug, then fix it; the test prevents regression.
  • Refactoring — a passing test suite gives you the confidence to change internals without breaking behaviour.

Code Examples

✗ Vulnerable
// Code written first, tested after — gaps in coverage
class PriceCalculator {
    public function calculate(int $qty, float $price, float $discount): float {
        return ($qty * $price) * (1 - $discount / 100);
    }
}
// Tests written after often miss edge cases:
// What if qty is 0? discount > 100? price is negative?
✓ Fixed
// TDD — test written first reveals edge cases before coding
class PriceCalculatorTest extends TestCase {
    public function test_calculates_discounted_price(): void {
        $calc = new PriceCalculator();
        $this->assertEquals(90.0, $calc->calculate(1, 100.0, 10));
    }
    public function test_zero_quantity_returns_zero(): void {
        $this->assertEquals(0.0, (new PriceCalculator())->calculate(0, 100.0, 0));
    }
    public function test_rejects_discount_over_100(): void {
        $this->expectException(InvalidArgumentException::class);
        (new PriceCalculator())->calculate(1, 100.0, 110);
        // This test FAILS first — now implement the validation
    }
}

Added 23 Mar 2026
Edited 5 Apr 2026
Views 134
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 3 pings T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 2 pings T 3 pings F 0 pings S 1 ping S 0 pings M 0 pings T 1 ping W 2 pings T 1 ping F 0 pings S
No pings yet today
ChatGPT 1
ChatGPT 1.5k Amazonbot 17 Perplexity 14 Google 8 SEMrush 3 Majestic 2 Ahrefs 1
crawler 1.5k crawler_json 4
DEV INTEL Tools & Severity
🔵 Info ⚙ Fix effort: Medium
⚡ Quick Fix
Start every new feature with a failing PHPUnit test: php artisan make:test FeatureTest --unit — write the assertion first, then implement just enough to pass it
📦 Applies To
PHP 7.0+ web cli

✓ schema.org compliant