Blog | Zencoder – The AI Coding Agent

How to Get Started with Unit Testing in Laravel

Written by Sergio | Aug 1, 2025 10:40:53 AM

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!

Pre-Configured Testing in Laravel

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.

Unit Tests vs. Feature Tests in Laravel

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 (tests/Unit)

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:

  • Utility classes
  • Helper functions
  • Pure PHP logic
  • Custom services that are independent of Laravel

Feature Tests (tests/Feature)

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:

  • HTTP endpoints and API responses
  • Business logic that relies on database interactions
  • Integration between multiple classes or services

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

Setting Up Your Laravel Testing Environment

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:

  • APP_ENV set to "testing" – The PHPUnit configuration file (phpunit.xml) contains environment variables that override your normal .env settings when tests run. Notably, APP_ENV is set to testing. Laravel automatically recognizes this and will use the testing configuration for various services.
  • Database, Cache, Session isolation – By default, session and cache drivers are set to array (so nothing is written to disk), and mail is set to log or array to prevent real emails from being sent.

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.

Using a .env.testing File

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:

  • CACHE_DRIVER=array
  • SESSION_DRIVER=array
  • MAIL_MAILER=log
  • DB_CONNECTION=sqlite
  • DB_DATABASE=:memory:

Using .env.testing keeps your phpunit.xml cleaner and ensures everyone running tests, including CI systems, gets the correct environment automatically.

💡 Tip

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.

Create and Organize Tests in Laravel

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.

Create a Test Class

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.

Test Class Structure

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.

Naming Conventions

To keep things clear and consistent, it's helpful to follow some simple naming conventions:

  • Test class names – These should end with Test. For example: UserTest or InvoiceCalculatorTest.
  • Test method names – The easiest way to name test methods is to start them with test_ followed by a brief description of what they check. For instance:

Organize Your Test Files

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.

setUp and tearDown in Tests

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:

  • Call parent::setUp() at the beginning of your setUp() method.
  • Call parent::tearDown() at the end of your tearDown() method.

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.

How to Write a Unit Test in Laravel

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:

  • Arrange – Instantiate the Calculator class.
  • Act – Call the add method with the numbers 2 and 3.
  • Assert – Use $this->assertEquals(5, $result) to verify that the result matches the expected value.

PHPUnit offers various assertion methods. Some commonly used ones include:

  • $this->assertTrue($condition) / $this->assertFalse($condition) – for boolean checks.
  • $this->assertNull($value) – to verify a value is null.
  • $this->assertCount(3, $array) – to ensure an array has a specific number of elements.
  • $this->assertInstanceOf(SomeClass::class, $object) – to confirm object type.
  • $this->assertSame($expected, $actual) – like assertEquals, but also checks for type and identity.

Ways to Run Unit Tests in Laravel

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

💡 Want to write Laravel tests faster?

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.

Top 7 Best Practices and Common Pitfalls in Laravel Unit Testing

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.

1. Avoid real external services 

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.

2. Use model factories for test data 

Don’t manually create models with hardcoded data. Use model factories (e.g., User::factory()->make()) to quickly generate realistic and consistent test data.

3. Focus on behavior, not implementation

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.

4. Run tests often

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.

5. Avoid flaky tests

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.

6. Don’t overuse mocks

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.

7. Use descriptive test names

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.

Accelerate Laravel Unit Testing With Zencoder

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:

  • Our intelligent agents understand your app and interact naturally across the UI, API, and database layers.
  • As your code changes, Zentester automatically updates your tests, eliminating the need for constant rewriting.
  • From individual unit functions to full end-to-end user flows, every layer of your app is thoroughly tested at scale.
  • Zentester’s AI identifies risky code paths, uncovers hidden edge cases, and generates tests based on how real users interact with your app.

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.

Here are some of Zencoder’s key features to help you:

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:

  • Code Completion – Smart code suggestions keep your momentum going with context-aware, accurate completions that reduce errors and enhance productivity.
  • Code Generation – Produces clean, consistent, and production-ready code tailored to your project’s needs, perfectly aligned with your coding standards.
  • Code Review Agent – Continuous code review ensures every line meets best practices, catches potential bugs, and improves security through precise, actionable feedback.

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:

  • Build smarter – Create specialized agents for tasks like pull request reviews, testing, or refactoring, tailored to your architecture and frameworks.
  • Integrate fast – Connect to tools like Jira, GitHub, and Stripe in minutes using our no-code MCP interface, so your agents run right inside your existing workflows.
  • Deploy instantly – Deploy agents across your organization with one click, with auto-updates and shared access to keep teams aligned and expertise scalable.
  • Explore marketplace – Browse a growing library of open-source, pre-built agents ready to drop into your workflow, or contribute your own to help the community move faster.

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:

  • Quickly spotting and fixing bugs, cleaning up broken code, and smoothly handling tasks across your entire project.
  • Automating repetitive or complex workflows to save you time and effort.
  • Accelerating full app development so you can focus on the creative, high-impact work that truly matters.

Sign up for free and speed up the way you write, run, and scale tests in Laravel!