Skip links

How to Get Started with Unit Testing in Laravel using PHPUnit

In this comprehensive guide, we will delve into the fundamentals of unit testing within the Laravel framework, utilizing the powerful PHPUnit tool. Whether you’re a seasoned Laravel developer looking to enhance your testing skills or a newcomer eager to understand the importance of unit testing in laravel, this article will walk you through the essential steps to get started. Explore practical examples, best practices, and gain the confidence to write robust, error-free code by incorporating effective unit tests into your Laravel projects. Elevate your development workflow and ensure the reliability of your applications with this step-by-step tutorial.

Starting on the Right Foot

Step 1 Why You Need Automated Tests

Let’s kick off by understanding why automated tests matter. These aren’t just time-savers but crucial sectors in better project planning. By executing repetitive parts of code, they catch errors early, saving you the hassle. Think of it as having an assistant who reports to you about any possible flaws in your new app feature, ensuring it doesn’t break anything in the old ones. The more features you add, the more time-saving it gets. Trust me, your future self will thank you for writing these automated tests.

Step 2 Understanding the Difference: Feature Tests VS Unit Tests VS Others

Confused by the differences between Feature Tests, Unit Tests, and Others? Let’s simplify it. Laravel indeed simplifies these terms into two main categories: Feature Tests and Unit Tests.

Feature Tests mimic user behavior in your app. They access URLs, call APIs, fill forms – exactly what users do in real life.

Unit Tests, on the other hand, scrutinize specific non-public units of your code in isolation. These focus on testing methods in a class that perform some function, e.g., a method calculates the price of an order. The unit test for this would assert if the method returns correct results with different parameters.

It is key to understand that the rest, whether Integration Tests, Functional Tests, or Smoke Tests, are other forms of testing that serve different purposes. However, in the Laravel context, most gear towards either feature or unit testing.

Now, you’ve got the basics! Let’s move to practicing this on a new Laravel project.

Step 3: Creating a new Laravel project

Ready to create a new Laravel project? Let’s dive in! Using Docker and Laravel Sail, you can kick-start your project swiftly, irrespective of your operating system. First, open the terminal and run this command:

curl -s | bash

Voila! A fresh Laravel project is set up right in the directory where you executed the command. To access this project, enter:

cd laravel_testing-api

Once inside the project, run the sail up command with:

./vendor/bin/sail up

Ensure you halt any instances of Redis or MySQL if they’re functioning on your machine to avoid potential port errors. Laravel Sail will set up all you need in a Docker container: database, Redis, and even a mail server! Sit back while it takes a couple of minutes to build initially. If you see the Laravel development server starting, congratulations! Your Laravel Sail has launched successfully.

There you have it, a new Laravel project. Now let’s have a look at the default test setup Laravel provides out of the box. Stay with me!

Step 4: Examining the default test setup in Laravel

After setting up your Laravel project, it’s time to discover Laravel’s default test setup. Laravel, by default, ships with two directories for tests: Feature and Unit under the tests directory.

The Feature directory is for tests covering larger parts of your application. These could be HTTP requests to endpoints, checking if the correct data returns, if the user is correctly redirected, and if the correct views are rendered.

Unit directory houses tests that confirm if your smallest pieces of application logic are error-free. These tests usually focus on small isolated sections of your application, such as singular methods in classes.

Laravel even runs a dummy unit test by default to verify if your environment is correctly configured to run database transactions during tests. Not only that, but Laravel also includes a PHPUnit.xml file which contains its testing configuration, and a phpunit dependency in the composer.json file.

Here we end this phase. Next, we’ll begin creating our first test from scratch! Stay tuned.

Step 5: Creating a test from scratch

Creating your first test from scratch might seem daunting, but Laravel makes it straightforward.

To start, let’s create a feature test:

php artisan make:test CanAccessHomePageTest

This command generates a test file under tests/Feature.

Next, we’ll write a simple test to check if users can access the home page:

public function test_if_user_can_access_home_page(){
  $response = $this->get('/');

This test asserts whether the main page returns a 200 status, indicating successful HTTP requests.

Congrats on creating your first Laravel test! In the next section, we’ll delve into testing HTTP responses. Stay tuned!

Step 6: Testing HTTP responses

Understanding how to test HTTP responses is vital for creating sustainable web applications. Laravel provides a neat and expressive API for this. Let’s start by analyzing an example:

public function test_the_application_returns_a_successful_response(){

public function test_the_application_returns_a_successful_response(){
  $response = $this->get('/');

This simple test sends a GET request to the root URL and checks if the response has a successful 200 status code. Sometimes, a POST request might require passing some query parameters. Here’s how you can do that:

public function test_the_application_returns_a_successful_response_with_name(){
  $response = $this->post('/user', ['name' => 'Amy']);

In real-world applications, testing the response status alone isn’t sufficient. We also need to examine the response content and ensure it matches our expectations. We’ll delve deeper into this in upcoming sections. So, stay tuned!

Step 7: Testing your database

Now, let’s venture into testing database-related code—again, Laravel makes this surprisingly easy. Suppose you want to verify if a new user is added to the database. You’ll start by creating a test:

php artisan make:test UserTest

Then, you’d write the test as such:

public function test_a_new_user_can_be_added_to_the_database(){
  // Arrange: define the user's data
  $userData = ['name' => 'John', 'email' => ' [email protected]'];
  // Act: add the user to the database
  // Assert: check if the user now exists in the database
  $this->assertDatabaseHas('users', $userData);

Here, we used the assertDatabaseHas helper to confirm that the user we created exists in the ‘users’ database table.

Remember to configure your test database connection correctly. Laravel provides several options, such as SQLite in-memory database, MySQL test databases, and more.

There you go! Now you know the basics of testing your database in Laravel. Next, we’ll explore Laravel Dusk, a browser testing tool. Can’t wait? Join me on the next phase

Step 8: Testing in the browser with Laravel Dusk

Did you know Laravel ships with an in-built browser testing tool, Laravel Dusk? This gem makes testing JavaScript-powered pages a breeze.

Let’s start by installing it:

composer require --dev laravel/dusk

php artisan dusk:install

This pulls in the required components, Google ChromeDriver, and an example test at test/Browser/ExampleTest.php

You might face conflicts with your development environment. To avoid this, set up an application on a different port:

cp .env .env.dusk.local

Change the port number in the new .env.dusk.local file, and start the application on the same port:

php artisan serve --port=8010 &

Once the server begins, run the test!

php artisan dusk

This is how Laravel Dusk eases browser testing. I’ll elaborate more on writing browser tests with Laravel Dusk in a later chapter. Stay with us, we are just getting started!

Step 9: Mocking external APIs in Laravel

Real-world applications often interact with external APIs. While testing, making actual network requests can make tests flaky, slow, and dependent on the availability of third-party services. Let’s work around this challenge using Laravel’s HTTP fake feature.

See it in action! Create a new test:

./vendor/bin/sail php artisan make:test MockTest

Next, add the test_mock_http() function with:

public function test_mock_http() {
 '' => Http::response(
         'name' => 'Italy',
         'code' => 'IT'
 $response = Http::get('');
         'name' => 'Italy',
         'code' => 'IT'
 ); }

Here, we used Laravel’s Http facade to mock an external API response. This illustrates how Laravel’s mocking technique can be utilized to test any piece of code interacting with an API without actual network requests.

The learning journey continues on! Next, we will discover howto integrate testing into Github Actions. See you in the next phase!

Step 10: Testing your Laravel application with GitHub Actions

If you are serious about Continuous Integration (CI), then GitHub Actions should be on your list.

First, ensure you’ve Git installed and set up a repository for your project on GitHub:

git init -b main
git add . && git commit -m "initial commit"
git remote add origin <Your Github repository URL>
git branch -M main
git push -u origin main

The series of commands initialize a git repository at your project’s root, commit all files, and push your repository to GitHub.

Next, for a CI pipeline, create a .github/workflows directory. GitHub Actions will automatically look for workflow files here.

mkdir -p .github/workflows
code ./github/workflows/test.yml

Write your GitHub Action workflow in this test.yml file. Now, each time you commit a change to your repository, GitHub Actions will run your application tests on their servers.

Quite thoughtful, isn’t it? Wait until I take you to the next level by setting up the development environment for Laravel. Stick around!

unit testing in Laravel
Source: Cloudway

Setting Up the Scene

Creating a New Laravel Project

Creating a new Laravel project is the first step towards setting up your development environment.

Here’s how you do it using Laravel Sail, an excellent tool regardless of the operating system you are using, as long as Docker is installed. This tool will become your new best friend, trust me!

  1. Open your terminal.
  2. Run the following command: curl -s | bash.
  3. This command sets up a new Laravel project under your current directory titled ‘myapp’.

Voila! You have created a fresh Laravel project. The next step? Installing Laravel Dusk for browser testing! Stick around to find out more.

Installing Laravel Dusk

Installing Laravel Dusk is straightforward and needs just a few commands.

Firstly, ensure you’re in the right directory:

cd myapp

Then, to install Laravel Dusk, run:

./vendor/bin/sail composer require --dev laravel/dusk

After this, install the Dusk components:

php artisan dusk:install

With these steps, Laravel Dusk and its components are installed, including a Google ChromeDriver and a test example in the tests/Browser/ExampleTest.php directory.

Congrats! You’ve successfully installed Laravel Dusk on your Laravel project. Keep reading to learn how to set up your database next!

Setting Up the Database

In unit testing, setting up the database correctly is crucial to avoid frustrations. Don’t worry, Laravel makes this painless.

To start, configure these lines in your .env file:


Next, create a new file, test.sqlite, in your database directory. This is the test database file that Laravel will use when running tests.

Lastly, in tests/TestCase.php, include the DatabaseMigrations trait to correctly build the database before each test.

use DatabaseMigrations;

And there you have it! You’ve successfully set up your database for testing. In the next sessions, we’ll write tests for your Laravel application. Stay tuned!

Writing Tests for Your Laravel Application

Writing a Basic Test

Ready to jump into writing a basic test? Laravel enables creating a basic unit test case in no time.

Navigate to tests/Feature/ExampleTest.php to see a basic test:

public function test_example(){

The above test simply checks if true is true. Not very useful, right?

Let’s write a basic test that checks the status code of a response:

public function test_homepage_response(){
  $response = $this->get('/');

This test sends a GET request to your application’s homepage and asserts that the response status was 200 (OK). Now, you’ve written a useful test!

With this, we wrap up the basics of writing tests. Let’s get to testing our application in the next sections! Stick around for more.

Writing Browser Tests with Laravel Dusk

In the world of Laravel testing, Laravel Dusk isn’t one to overlook. With its simplicity and powerful features, writing browser tests becomes a breeze.

To get started, first, you’ll need to create a browser test:

php artisan dusk:make UserTest

This command will create a new Dusk test file under tests/Browser. Here’s a simple example of what a browser test could look like:

public function testExample()
    $this->browse(function (Browser $browser) {

In this example, Dusk will visit the root URL and confirm that the word ‘Laravel’ appears on the page. This approach effectively simulates an actual human’s interaction with your web page.

Congrats! You’ve learned to write basic browser tests. We’ll discuss more complex topics and methods to interact with your webpage in later sections. Stay with us!

Disabling Middleware While Testing

Sometimes, middleware can clash with your testing ambitions. No worries, Laravel equips you with WithoutMiddleware trait to disable all middleware for your tests.

Just include the trait in your test class:

use WithoutMiddleware;

Want to disable middleware for specific test methods? Simply, call the withoutMiddleware method in those tests:

public function testBasicExample()
    // Test code here

This tip will save you from a plethora of potential headaches and speed up your tests.

Next up, the aesthetics! Let’s move onto testing HTML views. Stay tuned!

Testing HTML Views

Testing HTML views in Laravel is simple and seamless. Let’s imagine we have a test.blade.php view, which expects a $name variable, and our route returns this view:

Route::get('/view-test', function () { return view('test', ['name' => 'Taylor']);});

To test this, we’ll create a new test class ViewTest.php:

./vendor/bin/sail php artisan make:test ViewTest

Next, we’ll add our test:

public function test_if_view_contains_Taylor() {
    $response = $this->get('/view-test');

In this example, $response visits the URL /view-test and checks if it returns the successful status code of 200. The assertSee method then verifies that the HTML response contains the word ‘Taylor’.

We’ll continue this engaging learning journey in the next sections. Come along!

Diving Deeper with Assertions

Understand and Using assertSee() & assertDontSee()

Ever wondered if a specific text is in your test’s HTML response? Worry not, Laravel got your back with assertSee() and assertDontSee().

To demonstrate, let’s take an example:

public function testWelcomeMessage()
   // Send a GET request to the /welcome route
   $response = $this->get('/welcome');
   // Assert that the response has a status code of 200
   // Assert that the text 'Welcome to Laravel!' is visible in the response
   $response->assertSee('Welcome to Laravel!');
   // Assert that the text 'Goodbye!' is not visible in the response

Here, assertSee checks whether ‘Welcome to Laravel!’ is present in your HTML response, while assertDontSee ensures ‘Goodbye!’ is absent.

You might want to use these functions often, as they efficiently verify whether your front-end is behaving as it should.

In the next chapter, we’ll explore other assertions. Join me to make this learning journey more fruitful!

The assertTrue() and assertFalse() Assertions

Let’s dive into two useful PHPUnit assertions, assertTrue() and assertFalse(). As the names suggest, they are useful for testing boolean values.

To illustrate, suppose you have a method in your Basket class has($item) that returns true or false if an item is in the basket or not.

We can write a PHPUnit test for this:

public function test_basket_contents() {

Here, we intended to test whether item_one exists in the basket (which should return true) and item_four doesn’t (which should return false).

Remember, these assertions can be incredibly handy when you need to verify a method returns the expected boolean result. Keep rolling with us, there’s more to come!

Using assertContains(), assertCount(), and assertEmpty()

In Laravel testing adventures, you’ll love playing with assertContains(), assertCount(), and assertEmpty() assertions. They validate arrays and can test various scenarios.

For instance, you might have a method that starts with the ‘$’ item in the Box class. assertContains() will validate if an expected value exists in the provided array. Here’s an example:

public function test_items_starting_with_t()
    $results = $box->startsWith('t');
    $this->assertCount(3, $results);
    $this->assertContains('toy', $results);
    $this->assertContains('torch', $results);
    $this->assertContains('tissue', $results);

This test retrieves all items starting with ‘t’ in the box and asserts that there are three items and that ‘toy’, ‘torch’, and ’tissue’ are among them.

Making arrays empty for testing? assertEmpty() checks if an array is empty:


This assertion confirms the array is empty when searching for items starting with ‘s’.

In essence, these assertions assure that your arrays behave as expected.

Great job so far! Readyfor more? In the next section, we’ll delve into the assertCount() assertion in detail. Keep going, you’re doing great!

Utilizing the assertCount() Assertion

The assertCount() assertion is a magic tool, particularly when testing arrays. As the name suggests, assertCount() checks the expected number of items in an array.

Imagine your Basket class has a method contents(), returning an array of items:

public function test_basket_contents()
    // Check the basket contains three items
    $this->assertCount(3, $basket->contents());

In this example, assertCount() checks if three items are in the basket.

Seeing assertCount() in action, you might want to keep this nifty tool in your testing toolbox. It’s a precise way of asserting the quantity of items in a collection.

Stay put! More scenarios are coming your way. Next, we’re going to handle real-life testing scenarios for HTTP responses. Hang tight!

Handling Real-Life Scenarios

Testing HTTP Responses

Let’s dive into real-world testing of HTTP responses in Laravel. The framework’s fluent API makes it super simple to test all aspects of HTTP responses.

To illustrate, say you have an endpoint /tasks that should return a JSON array of tasks.

You could write a Laravel test like:

public function test_tasks_endpoint()
    $response = $this->get('/tasks');
    $response->assertJsonStructure(['*' => ['id', 'title', 'completed']]);

In this example, we send a GET request to /tasks, verify the HTTP status is 200, and then check the structure of the JSON response.

But wait, there’s more! Laravel also integrates with Laravel Dusk for browser testing. We’ll move onto this exciting area next. Let’s jump in!

Writing Tests for the Login Endpoint

Writing tests for the login endpoint is a crucial step. By doing so, you can ensure your user authentication acts as expected.

Let us make a simple test for the login endpoint. Your AuthenticationTest.php file might look something like this:

public function testLogin(){

   $user = factory(User::class)->create([
       'email' => ' [email protected]',
       'password' => bcrypt('sample123'),

   $loginData = ['email' => ' [email protected]', 'password' => 'sample123'];

   $this->json('POST','api/login', $loginData, ['Accept' => 'application/json'])
           "user" => [
               'id', 'name', 'email', 'email_verified_at', 'created_at', 'updated_at',


public function testMustEnterEmailAndPassword(){
           "message"=> "The given data was invalid.", "errors"=>[
               'email'=>["The email field is required."],
'password'=>["The password field is required."]

We’ve created two methods, testMustEnterEmailAndPassword() and testLogin(), to ensure users can’t leave required fields empty and that the user is authenticated successfully.

Next, gear up for our next testing adventure where we uncover working with JSON APIs. Stick around!

Testing JSON APIs

Another real-world scenario you’ll often face during Laravel testing is dealing with JSON APIs. Laravel is a key player here, covering several helpers for JSON API testing.

We’ll utilize the json() method to make a POST request to /user while asserting that a particular array was returned in JSON format:

public function testBasicExample()
    $this->json('POST', '/user', ['name'=>'Sarah'])
             'created' => true,

The seeJson method translates the given array into JSON, then affirms that the JSON fragment exists within the entire JSON response given by the application.

What if we need an exact JSON match or a JSON structural match? Well, Laravel has got you covered with seeJsonEquals() and seeJsonStructure() methods. Ready for more?

Stay tuned as we jump into more complex cases! Let’s get empowered with some more Laravel PHPUnit testing.

Processing More Complex Cases

Creating Tests for Instant Push Notification(IPN) Service in Laravel

Adding push notifications to your Laravel application? Great! Testing the service is equally important. The best way is arguably by mocking and spying on it.

Let’s create a test for this. I’ll walk you through an example:

public function testPushNotificationService() {
    // Arrange: Create a fake service and an example task
    $service = \Mockery::spy('App\Services\PushNotificationService');
    $task = factory('App\Task')->create();
    // Act: Call the task created method
    // Assert: Check if the notify method was called

In the code above, we used Mockery to create a ‘spy’ for our ‘PushNotificationService’. We then call the ‘taskCreated()’ method on our task. Finally, we assert that the ‘notify()’ method on our service was called once.

Next, we’re exploring a fascinating topic—examining the role of Database Transactions in Laravel Testing. Let’s stay tuned!

Examining the Role of Database Transactions in Laravel Testing

Database transactions in Laravel testing are nothing short of a life-saver. They allow for reverting the database to its initial state after each test, ensuring data from previous tests don’t hinder the following ones.

First, let’s create a test class and run a rollback on the database after each test:

use Illuminate\Foundation\Testing\DatabaseMigrations;

class ExampleTest extends TestCase
    use DatabaseMigrations;

    public function testBasicExample()
        // Test code here

The DatabaseMigrations trait will handle running migrations before the next test.

Alternatively, you can wrap every test in a database transaction, an option Laravel conveniently provides with the DatabaseTransactions trait:

use Illuminate\Foundation\Testing\DatabaseTransactions;

class ExampleTest extends TestCase
    use DatabaseTransactions;

    public function testBasicExample()
        // Test code here

This trait wraps each test within a transaction, then rolls it back after the test completion, ensuring a clean state for the next test.

We’ll get onto managing external APIs mocking next. Do stick around!

Managing External APIs Mocking in Laravel

Managing mocks for external APIs during testing is essential to prevent actual network requests, allowing tests to be quick and isolated.

Laravel makes this quite simple with the HTTP Fake method. For instance, you could mock a third-party API that retrieves posts:

use Illuminate\Support\Facades\Http;

public function testExample()
      '' => Http::response([
        'title' => 'First Post',
        'body' => 'This is the body of the first post.',
      ], 200),

  // normal test code

In the example above, any HTTP call to will be intercepted, and our mocked response will be returned instead.

With these basics, you’ve laid a strong foundation in learning the ropes of creating and managing mocked external APIs with Laravel. Continue with us as we explore creating a CI pipeline in the next section. See you there!

Putting It All Together with Continuous Integration

Creating a CI Pipeline

Continuous Integration (CI) Pipeline is pivotal in keeping the code in the best quality by providing faster feedback cycles. Let’s create one for our Laravel project using Semaphore.

  1. Use the ‘+’ sign in Semaphore beside Projects to add your repository.
  2. Locate and click on ‘Choose’ for your repository.
  3. Pick the ‘Laravel starter workflow’.
  4. Click on ‘Customize it first’. We need to adjust the pipeline before it runs.

In the Workflow Builder screen, modify as you wish.

In the initial pipeline configuration:

  1. Click on the ‘Test’ block.
  2. Open the ‘Environment Variables’ section and add ‘APP_ENV = local’ using ‘+Add env_var’ link. It’s crucial to specify this isn’t a production environment; otherwise, Dusk will refuse to run.
  3. Click on ‘Run the Workflow’ and then ‘Start’.

Bingo, your pipeline starts!

In the next section, let’s explore running tests in parallel. Keep reading!

Running Tests In Parallel

Running tests in parallel is a vital part of the testing strategy. Laravel and PHPUnit execute tests sequentially within a single process. However, to reduce the duration it takes to run your tests, you may opt to run them in parallel across multiple processes. Here’s how:

Install the ‘brianium/paratest’ Composer package as a “dev” dependency:

composer require brianium/paratest --dev

Then, include the --parallel option when executing the test Artisan command:

php artisan test --parallel

By default, Laravel generates as many processes as there are available CPU cores on your machine. You can regulate the number of processes using the --processes option:

php artisan test --parallel --processes=4

You now have a setup capable of taking on tests at an increased rate. Up next, we’ll look into reporting test coverage. Gear up!

Reporting Test Coverage

Test coverage reports are key indicators of how much of your code is covered by tests. They let you know the regions untouched by any of your test cases.

PHPUnit integrates with Laravel to generate these reports. You can see the coverage report after running the tests by appending a --coverage-* argument to the phpunit command, like so:

vendor/bin/phpunit --coverage-text

This provides a textual representation of the coverage report. To generate an HTML report, use the --coverage-html argument:

vendor/bin/phpunit --coverage-html tests/coverage

You can simplify things by adding a script named test to the project’s composer.json file:

composer test

Next up, we’ll tackle how to manage when things don’t grow as planned during testing. Don’t skip it!

When Things Don’t Grow as Planned: Dealing with Failures

What If Tests Fail?

If a test fails, fret not; you’re not doing anything wrong. In fact, a failed test indicates your test suite is doing its job—catching errors.

If the expected results of a test don’t match the actual results, PHPUnit will display a ‘F’ symbol (in case of using --testdox). Besides, the console output will provide information regarding the failed test:

There was 1 failure:

1) Tests\Feature\ExampleTest::test_the_guests_cannot_view_the_dashboard

Expected status code 302 but received 200.

Take the answer and fix the point of failure, focusing on what the error message is showing. Once resolved, proceed to re-run the tests.

Keep in mind, a failed test is the first step towards a bug-free application. Updated tests and an error-free application are the ultimate goal. Keep the momentum going and join us for more on Laravel testing!

This website uses cookies to ensure you get the best experience on our website. By using this site, you agree to our use of cookies. Learn more