Back to Blog
Lesson 53 of the PHP: Modern PHP from the Ground Up course
PHPSeptember 10, 20264 min read

Unit Testing with PHPUnit: A Practical Guide for PHP Developers

Master unit testing with PHPUnit to automate code verification. Learn to write simple tests, run them from the CLI, and assert expected outcomes confidently.

PHPTestingPHPUnitTDDSoftware Quality
A close-up view of PHP code displayed on a computer screen, highlighting programming and development concepts.

Previously in this course, we covered creating a CLI utility. Now that you can automate scripts, we’ll take the next step: verifying those scripts—and your MVC application—with automated tests.

Manual testing is a trap. You change a method in your User model, refresh your browser, and hope you didn't break the login logic. As your project scales, this "hope-based" development becomes unsustainable. Unit testing with PHPUnit allows you to define the expected behavior of your code and verify it automatically every time you make a change.

What is Unit Testing?

A unit test isolates a small, specific part of your code—usually a single method—and asserts that it behaves exactly as expected. If you have a method calculateTotal($price, $tax), a unit test provides specific inputs and verifies the output.

When you run your test suite, PHPUnit runs these checks in milliseconds. If the output doesn't match your expectation, the test fails, alerting you immediately to the regression. This is the foundation of The Red-Green-Refactor Cycle: Master the TDD Workflow, a practice that keeps your codebase clean and reliable.

Installing PHPUnit

A close-up of a screwdriver on a wooden box, highlighting DIY and home improvement tools.

Since we are using Composer for autoloading with Composer, installing PHPUnit is straightforward. Run this in your project root:

Bash
composer require --dev phpunit/phpunit

The --dev flag tells Composer this dependency is only needed for development, not for the production server.

Writing Your First Test

Let’s test a simple helper class in our MVC app. Create a folder named tests/ in your project root. Inside, create a file tests/CalculatorTest.php.

PHP
<?php
use PHPUnit\Framework\TestCase;

class CalculatorTest extends TestCase
{
    public function testAddition()
    {
        $result = 1 + 1;
        $this->assertEquals(2, $result);
    }
}

Breaking Down the Test

  1. Inheritance: We extend PHPUnit\Framework\TestCase. This provides the assertion methods (like assertEquals) we need.
  2. Naming Convention: Methods must be public and prefixed with test so PHPUnit discovers them automatically.
  3. Assertions: assertEquals(expected, actual) is the most common assertion. If actual does not equal expected, PHPUnit will throw an error.

Running Your Tests

To run your tests, call the PHPUnit binary located in your vendor folder:

Bash
./vendor/bin/phpunit tests

You should see a green output confirming the test passed. If you change the assertion to $this->assertEquals(3, $result);, the test will fail, providing a clear "Red" state. This is exactly what we discuss when writing failing unit tests first—defining requirements before implementing the logic.

Practice Exercise

Adults in a yoga studio stretch on mats, promoting fitness and flexibility.

  1. Create a Math class inside your src/ folder with a method multiply($a, $b).
  2. Create a corresponding test file tests/MathTest.php.
  3. Write a test method that asserts multiply(2, 4) returns 8.
  4. Run the test suite and confirm it passes.

Common Pitfalls

  • Testing Logic vs. Implementation: Don't test that a private method exists; test that the public method returns the correct result. If you find yourself needing to test private methods, your code is likely too complex.
  • Side Effects: A unit test should be "pure." If your test writes to the actual database or sends emails, it’s an integration test, not a unit test. Keep unit tests fast by avoiding external dependencies.
  • Forgetting the test prefix: If your method is named addition() instead of testAddition(), PHPUnit will ignore it. Always prefix your test methods.

FAQ

Q: Can I use var_dump instead of testing? A: var_dump is a one-time check. Tests are repeatable documentation. Once you write a test, it protects that code from future bugs forever.

Q: How do I test my MVC Controllers? A: Controllers are harder to unit test because they often depend on request globals. Start by writing tests for your Models and helper classes first, as they contain the core business logic.

Q: My test is slow. What's wrong? A: You are likely hitting the network or database. Use Advanced TDD Patterns: Mastering Mocks and Complex Logic to simulate those slow dependencies.

Recap

Team members presenting a project in a modern office setting with a focus on collaboration.

We've moved from manual browser checks to automated verification. By installing PHPUnit and learning to write assertions, you now have the ability to catch bugs before they reach your users. Remember: tests are the safety net that allows you to refactor your MVC app with confidence.

Up next: We will explore the Service Container to manage dependencies and make our application even easier to test.

Similar Posts