Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Testing

After every change you need to make sure nothing broke. Checking a site by hand is slow, and something always gets forgotten. An automated test is code that calls your function and compares the result with what is expected. Hundreds of such checks run in seconds.

The PHPUnit examples need the library installed and cannot be run here.

The idea of a test

A test has three steps: prepare the data, perform the action, compare the result. This can be done without any library:

<?php
 
function applyDiscount(float $price, int $percent): float
{
    return $price * (100 - $percent) / 100;
}
 
$result = applyDiscount(200, 25);
 
echo $result === 150.0 ? 'test passed' : 'test failed';

A testing library does the same, but it finds the tests itself, runs them and shows a clear report.

Installing PHPUnit

PHPUnit is the standard testing library for PHP. It is installed as a development dependency:

composer require --dev phpunit/phpunit

Tests go into the tests folder and are run with the command:

vendor/bin/phpunit tests

The first test

A test is a class that extends TestCase. Every method whose name starts with test is a separate check:

<?php
// tests/DiscountTest.php
 
use PHPUnit\Framework\TestCase;
 
final class DiscountTest extends TestCase
{
    public function testAppliesDiscount(): void
    {
        $this->assertSame(150.0, applyDiscount(200, 25));
    }
 
    public function testZeroDiscountKeepsPrice(): void
    {
        $this->assertSame(200.0, applyDiscount(200, 0));
    }
}

assertSame() compares the expected value with the actual one strictly — by both value and type.

The main assertions

MethodWhat it checks
assertSame($expected, $actual)The values are equal and of the same type
assertTrue($value)The value is true
assertCount(3, $array)The array has three elements
assertContains('a', $array)The array contains the value
assertNull($value)The value is null

Testing exceptions

You also need to check that a function refuses bad data correctly:

<?php
// tests/AccountTest.php
 
use PHPUnit\Framework\TestCase;
 
final class AccountTest extends TestCase
{
    public function testCannotWithdrawMoreThanBalance(): void
    {
        $account = new BankAccount();
        $account->deposit(100);
 
        $this->expectException(InvalidArgumentException::class);
 
        $account->withdraw(500);
    }
}

The test passes only if an exception of the given type really occurs after expectException().

The report

If a check fails, PHPUnit shows which test it was and how the expected value differs from the actual one:

There was 1 failure:
 
1) DiscountTest::testAppliesDiscount
Failed asserting that 160.0 is identical to 150.0.

What to test

First of all, code with logic: calculations, validation, data transformation. Such code is easy to test when a function receives everything it needs through parameters and returns a result, rather than reading global variables or printing to the screen.

Common mistake

A test that checks only the "happy path" misses the main bugs. Test the boundaries: zero, an empty string, an empty array, a negative number, a value that is too large. That is where code breaks most often.

Thanks for reading!

Donate to the site — we work hard for you.