Are you building a Laravel app and wondering how to properly test your code?
If so, you’ll be glad to know that it comes pre-configured with powerful testing tools, such as PHPUnit and Pest, allowing you to get started right away. In this article, we’ll walk you through how to set up your testing environment, write your first unit test, and understand what makes a good unit test. Whether you're testing simple classes or complex application logic, this guide will give you everything you need to get started with Laravel unit testing!
One of the great things about Laravel is that it comes ready for testing from the very beginning. When you start a new Laravel project, it’s already set up with testing tools like PHPUnit and even Pest.
🟢 Pre-configured PHPUnit setup – Laravel includes a phpunit.xml file right at the root of your project. This file is already configured for Laravel and defines how your tests run, including default environment settings for testing.
🟢 Pest support – Laravel also supports the Pest testing framework out of the box. You can choose to write tests in Pest style or PHPUnit style (or even combine both in the same project).
🟢 Tests directory structure – Inside the tests folder, you'll see two subfolders: Unit and Feature. Each folder contains a basic example test to help you get started and ensure your testing setup is working properly.
🟢 Base test classes and helpers – Laravel feature tests extend a base class that sets up your application for testing. This gives you access to powerful helper methods, such as $this->get() or $this->post(), to simulate HTTP requests and interact with the database or app features easily.
🟢 PHPUnit assertions and Laravel additions – You can use all the usual PHPUnit assertions like $this->assertTrue() and $this->assertEquals(). Laravel also adds handy extras, such as $response->assertStatus(200) to verify a status code or $response->assertSee('Welcome') to check if a page contains specific text.
When writing tests in Laravel, it's important to understand the distinction between unit tests and feature tests, as they serve different purposes and operate in different ways.
Unit tests are designed to test small, isolated pieces of code without involving the Laravel framework. These tests extend PHPUnit’s base TestCase class and do not load the Laravel application, which makes them very fast and lightweight. They are ideal for testing:
Feature tests, on the other hand, are used to test how different parts of your Laravel application work together. These tests extend Laravel’s TestCase class, which boots the whole application, giving you access to everything Laravel provides. They are great for testing:
Test Type |
Speed |
Laravel Loaded |
Use For |
Unit Test |
🔹 Fast |
❌ No |
Isolated logic, pure PHP code, utility classes |
Feature Test |
🔸 Slower |
✅ Yes |
Routes, database, events, business logic |
When you run tests in Laravel, the framework automatically switches to a special testing environment. This ensures your tests don’t affect your real data, send actual emails, or interfere with your development or production setup. Laravel handles this by switching the configuration environment to “testing” whenever you run PHPUnit or Pest:
Most importantly, you need to set up a separate database for testing, often an in-memory SQLite database, to prevent interference with your development data.
Laravel even includes these settings in phpunit.xml, but they might be commented out by default:
Simply uncomment them to use a fast, in-memory SQLite database that resets with every test, eliminating the need for setup and minimizing the risk to your real data.
📌 Note
If your app uses features not supported by SQLite, configure a real test database (like MySQL) in .env.testing or phpunit.xml instead.
To keep test settings separate from your main environment, Laravel allows you to create a .env.testing file in the project root. When you run PHPUnit or Pest, Laravel will automatically load this file instead of your regular .env.
This is a great place to define settings like:
Using .env.testing keeps your phpunit.xml cleaner and ensures everyone running tests, including CI systems, gets the correct environment automatically.
If you’ve ever run php artisan config:cache, make sure to run php artisan config:clear after adding or changing .env.testing so Laravel picks up the new values.
Laravel makes it easy to create and organize test classes using built-in Artisan commands. Instead of manually creating test files and boilerplate code, you can generate fully structured test classes with a single command.
Run php artisan make:test SomeNameTest. This will create a new test file in the tests/Feature directory by default.
If you want to create a unit test class in the tests/Unit folder, add the --unit option:
php artisan make:test SomeNameTest --unit.
The generated file will already include the correct namespace (Tests\Feature or Tests\Unit), import statements, and a basic test method.
A typical unit test class in Laravel extends PHPUnit\Framework\TestCase and focuses on isolated pieces of logic, without relying on Laravel’s framework features, such as the service container, routes, or database.
Here's an example of a simple unit test:
Unit tests like this are ideal for testing classes such as services, utilities, or any logic that doesn't require the Laravel application to be bootstrapped.
To keep things clear and consistent, it's helpful to follow some simple naming conventions:
Keep related tests grouped together to make your test suite easier to navigate and manage. If you're testing a specific feature or component, you can organize those tests into a subfolder or use a consistent naming pattern.
Laravel already gives you a good starting point by separating tests into Unit and Feature folders. Inside those, you can mirror your app’s structure. For example, if you have a PaymentService in app/Services, you might place its unit test in tests/Unit/Services/PaymentServiceTest.php.
Each test method in your class runs in isolation. If you need to prepare or clean up anything before and after each test, you can override the setUp() and tearDown() methods.
When overriding these methods, always call the parent versions:
The base TestCase class uses setUp() to do important things like refreshing the application and running database migrations (if you're using traits like RefreshDatabase). Skipping the parent calls can lead to unexpected and hard-to-debug test issues.
Let’s walk through creating a simple unit test in Laravel. Suppose we have a very basic class that isn’t tied to Laravel’s core, for example, a calculator utility class:
This class just adds two numbers, and we want to write a unit test to verify that functionality. Here is how to do it in 2 easy steps:
✅ Step 1: Generate the test class – We use Artisan to create a new test class. Since this is a unit test not requiring the Laravel framework, we’ll put it in the Unit tests suite:
This will create a new file at: tests/Unit/CalculatorTest.php.
✅ Step 2: Write the test method – Open the new class file. It should look something like:
This test class extends PHPUnit\Framework\TestCase, which is appropriate for pure unit testing and does not rely on Laravel’s application container. The Calculator class from App\Utilities is imported so it can be used within the test.
The test method test_adds_two_numbers_correctly follows the Arrange-Act-Assert pattern:
PHPUnit offers various assertion methods. Some commonly used ones include:
Once you have finished writing your tests, you need to run them to verify that everything behaves as expected. Laravel provides multiple tools to run unit tests, each with its strengths:
Task |
Command |
Run all unit tests |
php artisan test --testsuite=Unit |
Run a specific test file |
php artisan test tests/Unit/MyTest.php |
Run a specific test method |
php artisan test --filter=test_method_name |
Stop after first failure |
php artisan test --stop-on-failure |
Run unit tests in parallel |
php artisan test --testsuite=Unit --parallel |
Meet Zenster, Zencoder’s AI-powered testing agent that automatically generates, updates, and maintains your unit tests.
Save hours of manual work and catch bugs before they reach production.
Now that you understand how Laravel unit testing works, it’s important to follow the best practices to write tests that are reliable, fast, and easy to maintain.
Unit tests should not make real API calls, send emails, or depend on outside systems. Instead, use Laravel’s tools like Http::fake() and mail fakes to simulate these services and keep tests fast and reliable.
Don’t manually create models with hardcoded data. Use model factories (e.g., User::factory()->make()) to quickly generate realistic and consistent test data.
Write tests that check what the code does, not how it does it. If you change the code structure but the behavior stays the same, your tests should still pass, that's how you know they're solid.
Make it a habit to run your tests regularly while coding. It’s much easier to fix a bug right after you write the code than to dig through a pile of failures later.
Flaky tests sometimes pass and sometimes fail for no clear reason, often due to randomness or time-based logic. Use tools like Carbon::setTestNow() to control time and keep your tests consistent.
Mocks can help isolate parts of your code, but too many make tests hard to understand and maintain. Only mock what you need to, like external services or very slow dependencies.
Use clear, meaningful names that describe what the test is checking. A name like test_user_must_be_verified_to_access_dashboard tells you more than testAccess.
Zencoder is an AI-powered coding agent that enhances the software development lifecycle (SDLC) by improving productivity, accuracy, and creativity through advanced artificial intelligence solutions. With Zenster, a testing agent that uses AI to automate testing at every level, your team can catch bugs early and ship high-quality code faster. Just describe what you want to test in plain English, and Zentester takes care of the rest, adapting as your code evolves.
Watch Zencoder auto-generate tests in action
Here is what it does:
At the heart of Zencoder is Repo Grokking™ technology, which thoroughly analyzes your entire codebase, identifying structural patterns, architectural logic, and custom implementations. This deep, context-aware understanding allows Zencoder to provide precise recommendations, significantly improving code writing, debugging, and optimization.
1️⃣ Integrations – Zencoder integrates with over 20 developer environments, streamlining the entire development lifecycle. It is the only AI coding agent offering this depth of integration.
2️⃣ Unit Test Generation – Create and run detailed unit tests with our AI-powered system. Ensure your code is reliable, accurate, and built to the highest standards.
3️⃣ All-in-One AI Coding Assistant – Boost your development workflow with a powerful AI tool that offers smart code suggestions, automatic code generation, and real-time code reviews:
4️⃣ Zen Agents – Fully customizable AI teammates that understand your code, integrate seamlessly with your existing tools, and can be deployed in seconds.
With Zen Agents, you can:
5️⃣ Multi-Repo Search – Index and search across multiple repositories so AI agents can understand and navigate complex multi-repo architectures. Easily add and manage repositories through the web admin panel, enabling agents to access and query all indexed code when needed.
6️⃣ Coding Agent – Say goodbye to tedious debugging and time-consuming refactoring. Our smart coding assistant helps you move faster and work smarter across multiple files by:
Sign up for free and speed up the way you write, run, and scale tests in Laravel!