PHPUnit Tutorial: Guide to Unit Testing with PHP

Learn PHPUnit essentials with our beginner's guide. Write better PHP tests and ensure robust, reliable code every time.

Last updated: 31 August 2026 15 min read

Key Takeaways

  • PHPUnit is a PHP unit testing framework that helps developers test individual functions, methods, and classes in isolation.
  • PHPUnit provides features such as assertions, test suites, test doubles, code coverage, and CI/CD integration to automate and organize PHP testing.
  • Effective PHPUnit testing involves writing focused, independent tests, covering edge cases, using appropriate test data and maintaining tests alongside code changes.

PHP applications rely on functions, methods, and classes to handle tasks such as authentication, calculations, and data processing.

Testing these components individually helps developers identify errors before they affect larger application workflows. This guide covers how to write and run PHPUnit tests, use assertions, and follow unit testing best practices.s.

What is PHPUnit?

PHPUnit is the most popular framework for unit testing in PHP. It provides developers with the tools they need to ensure that individual components of their application, such as functions and methods, behave as expected. It supports Test-Driven Development (TDD).

PHPUnit follows the xUnit architecture, which is a standard for unit testing frameworks, making it familiar to developers who have worked with testing tools in other programming languages like JUnit for Java or NUnit for .NET.

PHPUnit ensures each piece of code behaves correctly under conditions, including edge cases and invalid input. It also enables developers to create maintainable code, get detailed feedback, and reduce reliance on manual testing.

Example of PHPUnit Testing

For example, a PHP application might have a function that calculates a discount:

function calculateDiscount(float $price, float $discount): float 

{ 

return price - (price * $discount / 100); 

}

calculates a discount

A PHPUnit test can check whether the function returns the expected result:

public function test CalculateDiscount(): void 



{ 



$result = calculateDiscount(100, 20); 



$this->assertEquals(80, $result); 
}

CalculateDiscount

Here, the test provides known inputs, runs the function, and checks whether the returned value matches the expected result.

If a later code change alters the calculation incorrectly, the test can flag the regression.

What does PHPUnit Test?

PHPUnit is primarily used to test application logic at the code level, including functions and methods, classes, return values, exceptions, conditional logic, edge cases, and side effects with interactions and dependencies.

It is not a replacement for cross-browser or end-to-end testing. Instead, it focuses on verifying individual units of PHP code, like functions, methods, or classes, to ensure all units work correctly in isolation before being combined into larger application workflows.

For example, PHPUnit can verify that a PHP method correctly validates login credentials. It does not, by itself, open Chrome, enter a username into a login form, click a button, and verify the resulting page.

That distinction is important when building a complete testing strategy.

Why Use PHPUnit for PHP Unit Testing?

As a PHP application grows, manually checking every change becomes increasingly difficult. A developer may fix one function and unintentionally affect another part of the application.

PHPUnit helps by keeping a feedback loop for all tests and simplifying debugging. Here are some other benefits of PHPUnit.

  • Catch defects early: PHPUnit identifies incorrect application logic as soon as a change causes an expected result to fail.
  • Test edge cases: Developers can create specific tests for conditions that may be difficult to reproduce manually, such as empty values, invalid inputs, boundary conditions, or exceptions.
  • Detecting regressions: Once a UI component behavior is covered by a test, the same test can be run after future code changes to check whether that behavior still works.
  • Make code refactoring safer: PHPUnit tests act as a safety check and fail when existing behavior breaks. This allows developers to improve or restructure code with confidence while preserving functionality.
  • Automate repetitive checks: Instead of manually repeating the same validation after every change, PHPUnit can execute the tests automatically.
  • Support CI/CD pipeline: PHPUnit tests can be incorporated into development and CI/CD pipelines so that code changes are validated automatically before they move further through the delivery process.

Key Features of PHPUnit

Below are the key features of PHPUnit:

  1. xUnit-Based Architecture: Built on the standardized xUnit framework, making it intuitive for developers familiar with other unit testing tools like JUnit or NUnit.
  2. Rich Assertions: Provides a variety of assertions to test different conditions, such as equality, truth, exceptions, and array contents.
  3. Test Suites: Organize and run groups of related test cases for better structure and manageability.
  4. Test Doubles Support: Offers mocks, stubs, and fakes for simulating objects and dependencies during tests.
  5. Code Coverage Analysis: Identifies parts of the code that are tested and highlights untested areas.
  6. Integration Ready: Works seamlessly with CI/CD tools like Jenkins, GitHub Actions, and GitLab CI.

Creating a Basic Test Case with PHPUnit

A test case is a single scenario designed to validate a specific piece of functionality in your app.

In PHPUnit, each test case is a method inside a test class. That class uses PHPUnit’s built-in features, like assertions, setup/teardown hooks, and test runners—to check your code behaves as expected.

Steps to Create a Basic Test Case

Step 1: Set Up PHPUnit

PHPUnit needs to be installed and configured in your project. The easiest way to do this is via Composer:

composer require --dev phpunit/phpunit

You can also add a PHPUnit configuration file (phpunit.xml or phpunit.xml.dist) to your project root.

This file lets you define settings such as test directories, bootstrap files, and environment variables, so you don’t have to pass them as command-line flags every time you run your tests.

Step 2: Create the Class to Test

Before you can write tests, you need the class or function you’re testing. Here’s a simple Calculator class:

<?php



class Calculator

{

    public function add($a, $b)

    {

        return $a + $b;

    }



    public function subtract($a, $b)

    {

        return $a - $b;

    }

}

This class has two methods: add and subtract.

Step 3: Create a Test Class

PHPUnit test classes must extend PHPUnit\Framework\TestCase. Extending this base class gives your test class access to PHPUnit’s assertion methods and test lifecycle features.

<?php



use PHPUnit\Framework\TestCase;



class CalculatorTest extends TestCase

{

    // Test methods go here

}

Step 4: Write Test Methods

Each test method should validate one specific behavior of the class under test. By convention, test method names start with “test” (or use the #[Test] attribute in newer PHPUnit versions) so the test runner can discover them automatically.

Here’s a test for the add method:

public function testAddition()

{

    $calculator = new Calculator();

    $result = $calculator->add(2, 3);



    $this->assertEquals(5, $result, "Addition did not return the expected result.");

}

assertEquals() compares the expected value (5) with the actual value returned by the method. The optional third argument is a custom failure message that’s shown if the assertion fails, which makes debugging faster.

Step 5: Complete Flow

Putting it all together, here’s a full test class with tests for both add and subtract:

<?php



require_once 'Calculator.php';



use PHPUnit\Framework\TestCase;



class CalculatorTest extends TestCase

{

    public function testAddition()

    {

        $calculator = new Calculator();

        $result = $calculator->add(2, 3);



        $this->assertEquals(5, $result);

    }




    public function testSubtraction()

    {

        $calculator = new Calculator();

        $result = $calculator->subtract(5, 3);




        $this->assertEquals(2, $result);

    }

}

Complete Flow

You can run this test class from the command line with:

./vendor/bin/phpunit CalculatorTest.php

If both assertions pass, PHPUnit will report OK (2 tests, 2 assertions).

Understanding Assertions in PHPUnit

An assertion is a statement in your test case that checks a condition. It compares actual outcomes (from your code) against expected outcomes (defined in your test) to verify correctness.

Assertions are critical because they determine whether a test passes or fails.
PHPUnit provides many assertions to test different conditions.

Below are some commonly used assertions:

  • assertEquals($expected, $actual, $message) : Checks if two values are equal.
$this->assertEquals(10, $result, "The result is not equal to 10.");
  • assertNotEquals($expected, $actual, $message) : Ensures that two values are not equal.
$this->assertNotEquals(0, $result, "The result should not be 0.");
  • assertTrue($condition, $message): Verifies that a condition is true.
$this->assertTrue(is_array($result), "The result is not an array.");
  • assertFalse($condition, $message): Verifies that a condition is false.
$this->assertFalse(empty($result), "The result is unexpectedly empty.");
  • assertNull($value, $message): Ensures a value is null.
$this->assertNull($result, "The result is not null.");
  • assertNotNull($value, $message): Ensures a value is not null.
$this->assertNotNull($user, "User object should not be null.");
  • assertContains($needle, $haystack, $message): Checks if a value exists within a given array or string.
$this->assertContains(5, $array, "The array does not contain the expected value.");
  • assertCount($expectedCount, $array, $message): Verifies the number of elements in an array or Countable object.
$this->assertCount(3, $items, "The array does not have 3 elements.");
  • assertInstanceOf($expectedClass, $object, $message): Ensures that an object is an instance of a specific class.
$this->assertInstanceOf(User::class, $user, "The object is not an instance of the User class.");
  • assertSame($expected, $actual, $message): Verifies that two values are identical (same type and value).
$this->assertSame('5', $result, "The result is not exactly the same as '5'.");

How to Run and Interpret PHPUnit Tests

Once you’ve written your PHPUnit tests, the next step is to run them and interpret the results to ensure your code works as expected. PHPUnit provides a command-line interface to execute tests and offers detailed feedback on the outcomes.

1. After creating your tests, run PHPUnit from your project’s root directory.

2. Run all tests:

vendor/bin/phpunit

Run all tests

3. To run a specific test file, provide its path:

vendor/bin/phpunit tests/CalculatorTest.php

PHPUnit executes the tests and reports whether they passed, failed, produced errors, or were skipped.

Understanding PHPUnit Test Results

ResultWhat it meansWhat to do
PassedThe actual result matched the expected result.No action is needed for that test.
FailedAn assertion produced a different result than expected.Check the failure message and the code being tested.
ErrorPHPUnit encountered an unexpected problem while running the test.Check for issues such as missing classes, methods, or dependencies.
SkippedThe test was intentionally not executed because a prerequisite was unavailable or the test was marked to skip.Check why the test was skipped before considering the run complete.

For example, a successful test run may return:

OK (3 tests, 5 assertions)

A failed assertion might look like

1) CalculatorTest::testAddition

Failed to assert that 4 matches were expected, 5.



/path/to/tests/CalculatorTest.php:15

This indicates that the test expected 5, but the code returned 4. The file path and line number help locate the failing assertion.

An error may indicate a problem such as a missing method:

Error: Call to undefined method Calculator::divide()

PHPUnit may also report skipped or incomplete tests:

OK, but incomplete, skipped, or risky tests!

Tests: 3, Assertions: 5, Skipped: 1.

A passing PHPUnit run only confirms the behaviors covered by those tests. It does not mean the entire PHP application has been validated.

Best Practices for Unit Testing in PHP

Follow these practices when writing and maintaining PHPUnit tests:

  • Give tests descriptive names: Name tests after the behavior they verify, such as testUserCanLogInWithValidCredentials().
  • Test one behavior at a time: Keep each test focused on a single scenario so failures are easier to trace.
  • Keep tests independent: Avoid making one test depend on the result or test execution order of another.
  • Cover edge cases: Test invalid inputs, boundary values, empty values, and expected exceptions alongside common scenarios.
  • Use data providers for repeated scenarios: Run the same test with different inputs instead of duplicating test methods.
  • Keep test data relevant: Use realistic, easy-to-understand values and avoid unnecessary data that makes tests harder to read.
  • Use test doubles when needed: Use mocks, stubs, or fakes when external dependencies such as databases or APIs need to be isolated.
  • Avoid testing implementation details: Test what the code does rather than how it is internally structured, so tests remain useful when the code is refactored.
  • Keep unit tests isolated from external systems: Avoid relying on live databases, APIs, or other services unless the test specifically requires them.
  • Review and update tests with code changes: When application behavior changes, update the corresponding tests so they continue to reflect the intended behavior.

Real-Device Testing for PHP Web Applications

PHPUnit validates PHP code in isolation, while real-device testing checks how the web application behaves in actual browser and device environments. Real-device testing can help identify:

  • Browser and OS differences: Features may behave differently across browser and operating system combinations.
  • Hardware limitations: Older or lower-powered devices may expose performance issues that aren’t visible on desktop systems.
  • Screen sizes and resolutions: Responsive layouts may render differently across device sizes.
  • Touch interactions: Buttons, forms, menus, and other interactive elements may behave differently with touch input.
  • Device-specific issues: Some problems only appear on particular devices or operating system versions.
  • Real-world performance: Network, hardware, and device conditions can affect how quickly pages and features respond.

For example, PHPUnit can verify that a PHP method returns the expected value, but it cannot verify whether the page displaying that value renders correctly on a mobile device.

A complete PHP web application testing strategy can combine PHPUnit for code-level testing with browser and real-device testing for user-facing behavior.

Common PHPUnit Testing Alternatives

PHP developers can choose from several testing tools depending on the type of testing they need and their preferred testing approach. The following table compares PHPUnit alternatives and complementary tools.

ToolBest forKey strengthRecommended when
PHPUnitGeneral PHP unit testingComprehensive features and ecosystemYou need a widely adopted framework for most PHP unit-testing requirements.
PestSimple, expressive testingClean syntax built on PHPUnitYou prefer a simpler syntax while remaining within the PHPUnit ecosystem.
CodeceptionUnit, functional, and acceptance testingMultiple testing types in one frameworkYou need to manage multiple types of testing within a single framework.
PHPSpecBehavior-driven development (BDD)Specification-first testing approachYour workflow follows a specification-first or behavior-driven approach.
MockeryMocking dependenciesFlexible test doubles and mocksYou need to isolate components with complex dependencies.
BehatAcceptance testing and BDDHuman-readable test scenariosYou need business-readable scenarios for acceptance or behavior-driven testing.
AtoumLightweight unit testingFast setup and executionYou prefer a lightweight framework with a simple setup.
SimpleTestLegacy PHP projectsLightweight framework for older codebasesYou are maintaining an older PHP application that already uses SimpleTest.

Conclusion

PHPUnit helps developers verify that individual application components behave as expected, making it easier to catch defects early and maintain code quality as the application evolves.

By writing clear, maintainable tests and integrating them into the development workflow, teams can identify issues earlier, reduce regression risks, and make changes to PHP applications with greater confidence.

Version History

  1. Aug 29, 2026 Current Version

    Refreshed and expanded the guide with updated information, practical guidance, and relevant resources.

    Abdul Qadir Khan
    Reviewed by Abdul Qadir Khan Senior Automation Expert
Tags
Automation Testing Testing Tools Website Testing
Manoj Kumar Masini
Manoj Kumar Masini

Senior Automation Expert

Manoj Kumar is an Senior Automatiom expert with 7+ years of experience in test automation and quality engineering. He writes about automation testing, QA best practices, and strategies for building reliable, scalable software delivery pipelines.

Automation Tests on Real Devices & Browsers
Seamlessly Run Automation Tests on 3500+ real Devices & Browsers