Testing with PHPUnit

Roman Huliak
3 min readFeb 6, 2025

--

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:

  1. Happy Path — Tests normal and expected behavior.
  2. Edge Cases — Tests boundary values (e.g., maximum and minimum inputs).
  3. 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. 🙂

--

--

Roman Huliak
Roman Huliak

Written by Roman Huliak

Full Stack Developer with 15 years of experience in ERP systems, skilled in leadership, analysis, and end-to-end development.

No responses yet