Writing robust PHP code starts with proper testing. In this comprehensive guide, I’ll walk you through everything you need to know about PHPUnit – from installation to advanced testing techniques that will revolutionize your development workflow.
I’ve been using PHPUnit for many years now, and it’s absolutely transformed how I approach PHP development. Testing isn’t just some optional extra – it’s essential for writing reliable, maintainable code. While the official PHPUnit documentation is good, it can be overwhelming for beginners. That’s exactly why I created this guide – to give you a clear, straightforward path to mastering unit testing in PHP.
PHPUnit is the industry standard testing framework for PHP developers. It provides everything you need to verify your code works exactly as intended. Whether you’re building a small project or enterprise-level application, PHPUnit will become your best friend in catching bugs before they reach production.
Before diving in, you might wonder why PHPUnit dominates the PHP testing landscape. While alternatives like SimpleTest exist, PHPUnit offers unmatched flexibility, extensive documentation, and widespread community support. It’s used by major frameworks like Laravel, Symfony, and CodeIgniter, making it the de-facto standard for PHP testing.
The knowledge you gain from this tutorial transfers perfectly to any PHP project or framework that supports testing. Plus, understanding PHPUnit concepts will help you grasp testing in other languages too – the core principles remain the same.
Installing PHPUnit is super easy. You have two main options:
I strongly recommend the project-specific approach using Composer. This ensures consistent testing across all development environments and avoids version conflicts between projects.
Here’s how to install PHPUnit in your project using Composer:
composer.json file:{
"require-dev": {
"phpunit/phpunit": "^10.5"
}
}Code language: JSON / JSON with Comments (json) bashcomposer update Note: I’m using PHPUnit 10.5 in this example, which requires PHP 8.1+. Check the compatibility chart to select the right version for your PHP installation.
To confirm PHPUnit installed correctly, run:
./vendor/bin/phpunit --version You should see output similar to:
PHPUnit 10.5.2 by Sebastian Bergmann and contributors.Code language: CSS (css) Congratulations! PHPUnit is now ready to use in your project.
While you can run PHPUnit tests directly from the command line, creating a configuration file makes testing much more efficient. The configuration file helps PHPUnit understand:
Let’s create a basic phpunit.xml file in your project’s root directory:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
bootstrap="tests/bootstrap.php"
colors="true"
stopOnFailure="false"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd">
<testsuites>
<testsuite name="Application Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>
<php>
<env name="APP_ENV" value="testing"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
</php>
</phpunit>Code language: HTML, XML (xml) This configuration:
tests/bootstrap.php as the bootstrap file (we’ll create this next)tests directoryThe bootstrap file loads any necessary dependencies or setup code before your tests run. Create a file at tests/bootstrap.php with:
<?php
// Include the Composer autoloader
require_once __DIR__ . '/../vendor/autoload.php';
// Any additional setup can go hereCode language: HTML, XML (xml) This simple bootstrap file ensures your autoloader is working correctly.
Let’s create a basic test to verify PHPUnit is working. In PHPUnit, test classes should:
CalculatorTest)PHPUnit\Framework\TestCasetestAddition())Create a file at tests/HelloWorldTest.php:
<?php
use PHPUnit\Framework\TestCase;
class HelloWorldTest extends TestCase
{
public function testTrueIsTrue()
{
$this->assertTrue(true);
}
}Code language: HTML, XML (xml) This test simply verifies that true is true – not very useful but perfect for confirming PHPUnit works.
Now run the test from your command line:
./vendor/bin/phpunit If everything is set up correctly, you’ll see output like:
PHPUnit 10.5.2 by Sebastian Bergmann and contributors.
. 1 / 1 (100%)
Time: 00:00.002, Memory: 4.00 MB
OK (1 test, 1 assertion) That green dot represents your passing test. If you see this, PHPUnit is working perfectly!
Assertions are the heart of unit testing. They verify that your code produces the expected results. PHPUnit provides many assertion methods for different testing scenarios. Here are the most common ones:
| Assertion Method | Description |
assertTrue($condition) | Checks that a condition is true |
assertFalse($condition) | Checks that a condition is false |
assertEquals($expected, $actual) | Checks that two values are equal |
assertSame($expected, $actual) | Checks that two values are identical (same type and value) |
assertNotEquals($expected, $actual) | Checks that two values are not equal |
assertNull($value) | Checks that a value is null |
assertNotNull($value) | Checks that a value is not null |
assertEmpty($value) | Checks that a value is empty |
assertCount($expectedCount, $array) | Checks that an array has the expected number of elements |
Let’s see how to use these assertions in a more practical example:
<?php
use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase
{
public function testAddition()
{
$result = 2 + 2;
$this->assertEquals(4, $result);
}
public function testStringComparison()
{
$string1 = "PHPUnit";
$string2 = "PHPUnit";
$this->assertSame($string1, $string2);
}
}Code language: HTML, XML (xml) These tests verify basic arithmetic and string comparison operations.
When testing, you’ll often need to prepare data before each test and clean up afterward. PHPUnit provides special methods for this:
setUp(): Runs before each test methodtearDown(): Runs after each test methodsetUpBeforeClass(): Runs once before all tests in the classtearDownAfterClass(): Runs once after all tests in the classHere’s an example:
<?php
use PHPUnit\Framework\TestCase;
class UserTest extends TestCase
{
protected $db;
protected function setUp(): void
{
// This runs before each test
$this->db = new Database();
$this->db->clearTestData();
}
protected function tearDown(): void
{
// This runs after each test
$this->db->disconnect();
$this->db = null;
}
public function testUserCreation()
{
$user = new User("John", "Doe");
$this->assertEquals("John", $user->getFirstName());
}
}Code language: HTML, XML (xml) This ensures each test starts with a clean database and properly disconnects afterward.
Once you’ve mastered the basics, you can explore these powerful PHPUnit features:
Data providers let you run the same test with different inputs:
<?php
use PHPUnit\Framework\TestCase;
class MathTest extends TestCase
{
/**
* @dataProvider additionProvider
*/
public function testAddition($a, $b, $expected)
{
$this->assertEquals($expected, $a + $b);
}
public function additionProvider()
{
return [
[1, 1, 2],
[0, 0, 0],
[-1, 1, 0],
[1.5, 2.5, 4.0]
];
}
}Code language: HTML, XML (xml) This runs the testAddition method four times with different inputs and expected results.
Unit tests should test classes in isolation. PHPUnit’s mocking system helps you create fake versions of dependencies:
<?php
use PHPUnit\Framework\TestCase;
class UserServiceTest extends TestCase
{
public function testGetFullNameWithMock()
{
// Create a mock of the Database class
$mockDatabase = $this->createMock(Database::class);
// Configure the mock to return predefined data
$mockDatabase->method('getUserById')
->willReturn(['first_name' => 'John', 'last_name' => 'Doe']);
// Inject the mock into the service
$userService = new UserService($mockDatabase);
// Test the getFullName method
$fullName = $userService->getFullNameById(1);
$this->assertEquals('John Doe', $fullName);
}
}Code language: HTML, XML (xml) This tests the UserService class without actually connecting to a database.
Sometimes you need to verify that your code throws exceptions when expected:
<?php
use PHPUnit\Framework\TestCase;
class DivisionTest extends TestCase
{
public function testDivisionByZero()
{
$this->expectException(DivisionByZeroError::class);
$calculator = new Calculator();
$calculator->divide(10, 0);
}
}Code language: HTML, XML (xml) This test passes only if the divide method throws a DivisionByZeroError.
As your project grows, good test organization becomes crucial. Here are some best practices:
src/User/UserService.php, create tests/User/UserServiceTest.phptestUserCannotLoginWithInvalidCredentials()Here’s an example of structured test organization:
project/
├── src/
│ ├── User/
│ │ ├── User.php
│ │ └── UserService.php
│ └── Order/
│ ├── Order.php
│ └── OrderService.php
├── tests/
│ ├── User/
│ │ ├── UserTest.php
│ │ └── UserServiceTest.php
│ └── Order/
│ ├── OrderTest.php
│ └── OrderServiceTest.php
└── phpunit.xml To get the most from PHPUnit, follow these proven best practices:
assertEquals(5, $result) is better than assertTrue($result == 5)PHPUnit is a powerful tool that will improve your PHP development workflow dramatically. By following this tutorial, you’ve learned how to set up PHPUnit, write basic and advanced tests, and organize your test suite effectively.
Testing might seem like extra work at first, but I can promise you’ll save countless hours hunting bugs and gain confidence when making changes to your codebase. As you continue your PHPUnit journey, explore the official documentation for more advanced features.
Remember, the best way to learn testing is by doing it. Start by adding tests to your existing projects and aim to write tests alongside new code. Your future self will thank you!
Ready to level up your PHP skills further? Check out my other tutorials on advanced PHP topics, design patterns, and framework-specific guides.
Learn python file handling from scratch! This comprehensive guide walks you through reading, writing, and managing files in Python with real-world examples, troubleshooting tips, and…
You've conquered the service worker lifecycle, mastered caching strategies, and explored advanced features. Now it's time to lock down your implementation with battle-tested service worker…
Unlock the full potential of service workers with advanced features like push notifications, background sync, and performance optimization techniques that transform your web app into…
This website uses cookies.
View Comments
Without class ,How to test PHP functions?
I have following simple code inside my Index.php file
So how to test only message() functin without class by using PHPUnit inside NetBeans Framework.
This will help you
http://stackoverflow.com/questions/36016250/is-it-possible-to-unit-test-php-files-other-than-class-file-using-phpunit/36016329#36016329
$this->assertEqual(50, $company->noOfEmployee());
this should be 'assertEquals'
but further than that, everything worked thanks!
Nice guide but I think you forgot to mention that before starting the test process, a proper directory structure for the files to be tested is required (mentioned here: https://www.cloudways.com/blog/getting-started-with-unit-testing-php/ ). This way you can efficiently run the test of your code.