Understanding the TDD Cycle

The TDD cycle consists of three main steps, often referred to as Red-Green-Refactor:

  1. Red: Write a failing test that defines a function or improvement.
  2. Green: Write the minimum amount of code to pass the test.
  3. Refactor: Clean up the code while keeping it functional.

Example Scenario: Building a Simple Calculator

Let’s create a simple calculator class that can perform addition and subtraction. We will follow the TDD cycle to implement this functionality.

Step 1: Setting Up PHPUnit

First, ensure that you have PHPUnit installed. You can install it via Composer:

composer require --dev phpunit/phpunit

Step 2: Write the Initial Test (Red Phase)

Create a new test file named CalculatorTest.php in the tests directory:

<?php

use PHPUnit\Framework\TestCase;

class CalculatorTest extends TestCase
{
    public function testAddition()
    {
        $calculator = new Calculator();
        $this->assertEquals(4, $calculator->add(2, 2));
    }

    public function testSubtraction()
    {
        $calculator = new Calculator();
        $this->assertEquals(0, $calculator->subtract(2, 2));
    }
}

Step 3: Run the Test

Run the PHPUnit test to see it fail:

./vendor/bin/phpunit tests/CalculatorTest.php

You should see output indicating that the tests are failing because the Calculator class does not exist yet.

Step 4: Write the Minimum Code (Green Phase)

Now, create the Calculator.php class in the src directory:

<?php

class Calculator
{
    public function add($a, $b)
    {
        return $a + $b;
    }

    public function subtract($a, $b)
    {
        return $a - $b;
    }
}

Step 5: Run the Test Again

Run the tests again:

./vendor/bin/phpunit tests/CalculatorTest.php

This time, the tests should pass, indicating that the add and subtract methods work correctly.

Step 6: Refactor the Code

In this case, our code is already quite simple, but let's say we want to add type hints for better clarity and type safety:

<?php

class Calculator
{
    public function add(int $a, int $b): int
    {
        return $a + $b;
    }

    public function subtract(int $a, int $b): int
    {
        return $a - $b;
    }
}

Step 7: Run the Tests Again

After refactoring, run the tests once more to ensure everything still works:

./vendor/bin/phpunit tests/CalculatorTest.php

Additional Testing Scenarios

You can extend your tests to cover edge cases and invalid inputs. For example, testing with negative numbers or non-integer values:

public function testAdditionWithNegativeNumbers()
{
    $calculator = new Calculator();
    $this->assertEquals(0, $calculator->add(-2, 2));
}

public function testSubtractionWithInvalidInput()
{
    $this->expectException(TypeError::class);
    $calculator = new Calculator();
    $calculator->subtract("two", 2);
}

Summary of TDD Benefits

BenefitDescription
Improved Code QualityTDD encourages writing clean, maintainable code.
Clear RequirementsTests serve as documentation for expected functionality.
Easier RefactoringWith tests in place, you can refactor with confidence.
Faster DebuggingFailing tests quickly highlight where issues occur.

Conclusion

Test-Driven Development in PHP using PHPUnit can significantly enhance your development process. By following the Red-Green-Refactor cycle, you ensure that your code is not only functional but also robust and maintainable. This approach is particularly beneficial in larger projects where requirements may evolve over time.

Learn more with useful resources: