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.

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

Since we are using Composer for autoloading with Composer, installing PHPUnit is straightforward. Run this in your project root:
Bashcomposer 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
- Inheritance: We extend
PHPUnit\Framework\TestCase. This provides the assertion methods (likeassertEquals) we need. - Naming Convention: Methods must be public and prefixed with
testso PHPUnit discovers them automatically. - Assertions:
assertEquals(expected, actual)is the most common assertion. Ifactualdoes not equalexpected, 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

- Create a
Mathclass inside yoursrc/folder with a methodmultiply($a, $b). - Create a corresponding test file
tests/MathTest.php. - Write a test method that asserts
multiply(2, 4)returns8. - 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
testprefix: If your method is namedaddition()instead oftestAddition(), 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

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.
Work with me

Laravel SaaS MVP & Multi-Tenant App Development
Launch your SaaS MVP on Laravel — multi-tenant, subscription-ready, and built by the engineer behind a platform serving 10,000+ paying users.

Custom WordPress Plugin Development
Custom WordPress & WooCommerce plugins built to standard — by the developer behind a plugin with 5,000+ active installs and a SaaS with 10,000+ users.

