Testing with PHPUnit
Testing is an essential part of software development and helps identify bugs before they reach production. In PHP, the most commonly used unit testing framework is PHPUnit.
Installing PHPUnit
If you don’t have it installed yet, you can add it via Composer:
composer require --dev phpunit/phpunit
After successful installation, you can check its version:
vendor/bin/phpunit --version
Basic PHPUnit Configuration
To facilitate test execution, you can create a phpunit.xml
file in the root directory of your project. This file allows you to set various configurations for PHPUnit.
Example of a basic phpunit.xml
configuration:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php"
colors="true"
verbose="true">
<testsuites>
<testsuite name="Unit Tests">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>
This configuration:
- Loads
vendor/autoload.php
to enable class autoloading. - Enables colored output and detailed test results.
- Defines the
tests/
directory as the location for test files.
Basic Test Structure
Tests in PHPUnit are classes that extend PHPUnit\Framework\TestCase
. Each test method must either have the test
prefix in its name or use the @test
annotation.
Example of a simple test:
use PHPUnit\Framework\TestCase;
class MathTest extends TestCase
{
public function testAddition()
{
$this->assertEquals(4, 2 + 2);
}
}
This test verifies that 2 + 2 equals 4.
Happy Path vs. Edge Cases vs. Failure Path
When testing, it is important to cover different scenarios:
- Happy Path — Tests normal and expected behavior.
- Edge Cases — Tests boundary values (e.g., maximum and minimum inputs).
- Failure Path — Tests incorrect inputs or unexpected situations.
Example: Testing a Calculator
Let’s create a Calculator
class and test its methods:
class Calculator
{
public function add($a, $b)
{
return $a + $b;
}
public function subtract($a, $b)
{
return $a - $b;
}
public function multiply($a, $b)
{
return $a * $b;
}
public function divide($a, $b)
{
if ($b == 0) {
throw new InvalidArgumentException("Cannot divide by zero");
}
return $a / $b;
}
}
Test class:
use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase
{
public function testAdditionHappyPath()
{
$calculator = new Calculator();
$this->assertEquals(5, $calculator->add(2, 3));
}
public function testSubtraction()
{
$calculator = new Calculator();
$this->assertEquals(1, $calculator->subtract(3, 2));
}
public function testMultiplication()
{
$calculator = new Calculator();
$this->assertEquals(6, $calculator->multiply(2, 3));
}
public function testDivisionHappyPath()
{
$calculator = new Calculator();
$this->assertEquals(2, $calculator->divide(6, 3));
}
vpublic function testDivisionByZeroFailurePath()
{
$this->expectException(InvalidArgumentException::class);
$calculator = new Calculator();
$calculator->divide(10, 0);
}
}
Running Tests
You can run tests using the following command:
vendor/bin/phpunit
If all tests pass successfully, you will see an output similar to:
PHPUnit 10.0.0 by Sebastian Bergmann.
..... 5 / 5 (100%)
Time: 00:00.012, Memory: 4.00 MB
OK (5 tests, 5 assertions)
Conclusion
Using PHPUnit helps maintain the reliability of your code and ensures that changes do not have unexpected side effects. In addition to basic tests, you can also use mocking, data providers, and integration testing scenarios.
Additional Testing Scenarios
- Testing large numbers: Ensuring that the calculator can handle large values without overflowing.
- Testing negative numbers: Checking if calculations work correctly with negative numbers.
- Testing floating-point precision: Ensuring proper handling of decimal values.
- Testing with invalid types: Verifying that the methods handle incorrect inputs like strings or null values gracefully.
It is a good practice to run tests regularly and integrate them into a CI/CD pipeline for automated code validation.
Thank you for taking the time to read my article. If you enjoyed it or have any suggestions for improvement, I’d love to hear your thoughts. This is my second attempt at writing, so I kindly ask for a bit of patience — every bit of feedback helps me grow. 🙂