Why Test?

Automated tests catch regressions, document behavior, and enable confident refactoring. PHPUnit is the standard testing framework for PHP.

Installation

  composer require --dev phpunit/phpunit
./vendor/bin/phpunit --version
  

Your First Test

  // tests/CalculatorTest.php
use PHPUnit\Framework\TestCase;

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

Run tests:

  ./vendor/bin/phpunit
  

Assertions

  $this->assertTrue($condition);
$this->assertFalse($condition);
$this->assertEquals('expected', $actual);
$this->assertSame(42, $value);           // strict (===)
$this->assertCount(3, $array);
$this->assertStringContainsString('foo', $haystack);
$this->assertInstanceOf(User::class, $obj);
$this->expectException(InvalidArgumentException::class);
  

Data Providers

Test multiple inputs without duplicating methods:

  /**
 * @dataProvider additionProvider
 */
public function testAdd(int $a, int $b, int $expected): void
{
    $this->assertEquals($expected, (new Calculator())->add($a, $b));
}

public static function additionProvider(): array
{
    return [
        [1, 1, 2],
        [0, 0, 0],
        [-1, 1, 0],
    ];
}
  

Mocking Dependencies

  public function testSendWelcomeEmail(): void
{
    $mailer = $this->createMock(MailerInterface::class);
    $mailer->expects($this->once())
           ->method('send')
           ->with($this->stringContains('Welcome'));

    $service = new UserService($mailer);
    $service->register('[email protected]');
}
  

Testing HTTP (Laravel)

  public function testHomePageReturns200(): void
{
    $response = $this->get('/');
    $response->assertStatus(200);
    $response->assertSee('Welcome');
}
  

Configuration

phpunit.xml:

  <?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php" colors="true">
    <testsuites>
        <testsuite name="Unit">
            <directory>tests/Unit</directory>
        </testsuite>
        <testsuite name="Feature">
            <directory>tests/Feature</directory>
        </testsuite>
    </testsuites>
</phpunit>
  

Best Practices

  • Arrange-Act-Assert structure in each test
  • One logical assertion per test when possible
  • Name tests descriptively: testUserCannotLoginWithWrongPassword
  • Run tests in CI on every pull request
  • Aim for high coverage on business logic; don’t chase 100% on trivial code

Testing is a hallmark of professional PHP development — start early and test often.