Testing is an important phase in the software development life cycle. It ensures
that the code you've written is working as designed before you move on to the
next part of your project. Every time you finish writing a software component,
you must also write a test to verify that its behavior matches your
expectations. This process helps maintain the quality of your code.
When we talk about testing in Laravel, we usually mean two things: unit testing
and feature testing. Unit testing only focuses on a small piece of code (usually
a method inside a class), while a feature tests verifies if a particular
feature, which may consist of multiple methods or classes interacting with each
other, is working the way you designed.
In this tutorial, we are going to talk about how to write unit tests in a
Laravel project using PHPUnit, the most popular unit testing package for PHP
projects. It is well integrated with Laravel, so no additional configuration is
required after you create a new Laravel project.
One thing to note is that even though PHPUnit is originally designed for unit
testing, it is completely okay if you use it for feature testing as well. The
package includes lots of powerful assertions that can be useful in many
different testing scenarios.
By following through with this tutorial, you will learn the following
aspects of unit testing in Laravel:
Creating and running tests.
Understanding how to use PHPUnit assertions.
Testing HTTP servers.
Testing both JSON and HTML APIs.
How to generate fake data for testing purposes.
Understanding browser testing with Laravel Dusk.
Mocking in Laravel.
Setting up a continuous integration test workflow.
Prerequisites
Before you proceed with this tutorial, ensure that you have met the following
requirements:
You have a basic understanding of PHP.
You have a code editor (such as
Visual Studio Code) installed on your
computer.
You have PHP (v8.0 or later) installed on your machine.
If you are using a Windows PC, make sure you have
WSL2 and
Docker Desktop installed
on your machine.
If you are using Mac, you only need to install
Docker Desktop.
You have Google Chrome installed
for browser testing through Laravel Dusk.
Sign up for a GitHub account if you don't have one
already. We'll be using it to set up a CI/CD workflow for testing in the final
step of this article.
Let's start by creating a new Laravel project from scratch. The Laravel team has
provided a tool called Laravel Sail that
allows us to quickly set up a project, no matter what operating system you are
using, as long as you have Docker installed and
running.
Open the terminal and run the following command to create a new project:
This will create a new Laravel project under the directory where you executed
the command. Next, go into the project you just created:
Copied!
cd <your_project_name>
In the project directory, run the sail up command as shown below. Ensure to
stop any instance of Redis or MySQL if they are running on your machine,
otherwise, you may get some errors about ports not being available.
Copied!
./vendor/bin/sail up
Laravel Sail will automatically install everything you need inside a Docker
container, such as a database, Redis, and even a mail server, and all we have to
do is wait. It could take several minutes to build the first time, but
subsequent builds will be much faster. If you see the following output, it means
that Laravel Sail was launched successfully.
laravel.test_1 | Starting Laravel development server: http://0.0.0.0:80
laravel.test_1 | [Thu Jun 2 17:37:32 2022] PHP 8.1.5 Development Server (http://0.0.0.0:80) started
Step 2 — Examining the default test setup in Laravel
Before we start writing tests, let's take a look at the default tests that
Laravel has provided for us. It will help us understand how testing works in
Laravel.
The phpunit.xml file
There is a phpunit.xml file in the project root directory. It is the
configuration file for PHPUnit, a testing framework for
PHP applications. It is divided into three main sections as shown below:
In the <testsuites> section, two different types of tests are predefined for
us. This section tells PHPUnit to run the tests that are stored in the
./tests/Unit and ./tests/Feature directories. We'll take a closer look at
both directories in the next step of this tutorial.
Next, the <coverage> section is where we define what code and what directories
are covered in the test, and how they are covered. It is not important for this
tutorial so we can leave it as is.
Finally, in the <php> section, we can define environment variables for the
testing environment. When running a test, the variables defined here will
overwrite the ones defined in the .env file that is also in the project root.
The tests Directory
The tests directory is where we store all our test files. Feature tests should
be in the tests/Feature directory, while unit tests should be in the
tests/Unit directory. PHPUnit will automatically look for and execute the
tests that are stored inside these directories as defined in the phpunit.xml
file. The CreatesApplication.php and TestCase.php files bootstrap the
application before running the tests. We don't need to change anything in these
files.
Let's take a look at an example test. Open the ./tests/Unit/ExampleTest.php
file in your text editor:
Copied!
code ./tests/Unit/ExampleTest.php
./tests/Unit/ExampleTest.php
Copied!
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function test_that_true_is_true()
{
$this->assertTrue(true);
}
}
This example test is quite basic as it tests if true is true. The
assertTrue() method is expecting a statement that returns the boolean value
true. This method is called an assertion, and PHPUnit provides us with many
other useful assertions, which will be discussed later in this article. You can
find a complete list of all PHPUnit assertions in their
official documentation.
To run this test, open a new terminal instance (so that Laravel Sail keeps
running) and execute the following command. Make sure you are at the root
directory of your project.
Copied!
./vendor/bin/phpunit
Output
Time: 00:00.123, Memory: 20.00 MB
OK (2 tests, 2 assertions)
This output indicates that Laravel ran two tests and two assertions (there is
another example test in the Feature directory, which includes another
assertion), and both are successful.
We can deliberately cause the test to fail by changing the argument that is
passed to the assertTrue() method as shown below:
./tests/Unit/ExampleTest.php
Copied!
. . .
public function test_that_true_is_true()
{
$this->assertTrue("this is a string");
}
. . .
Now run the test again:
Copied!
./vendor/bin/phpunit
Output
Time: 00:00.141, Memory: 20.00 MB
There was 1 failure:
1) Tests\Unit\ExampleTest::test_that_true_is_true
Failed asserting that 'this is a string' is true.
/Users/erichu/Documents/GitHub/laravel-unit-test/code/tests/Unit/ExampleTest.php:16
FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
When you undo the above changes, the test should pass again.
Next, let's try something more exciting by creating another test below the
test_that_true_is_true() function:
./tests/Unit/ExampleTest.php
Copied!
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
class ExampleTest extends TestCase
{
. . .
/**
* A basic test example.
*
* @return void
*/
public function test_that_name_is_jack()
{
$name = "John";
// $name = "Jack";
$this->assertTrue($name == "Jack");
}
}
Since the variable $name is tied with the value John, and John obviously
does not equal Jack, this test will fail.
Run this test with the following command:
Copied!
./vendor/bin/phpunit
Output
Time: 00:00.008, Memory: 8.00 MB
There was 1 failure:
1) Tests\Unit\ExampleTest::test_that_name_is_jack
Failed asserting that false is true.
/Users/erichu/Documents/GitHub/laravel-unit-test/code/tests/Unit/ExampleTest.php:29
FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
Before you proceed with the next step, change $name back to 'Jack' so that
you won't get failures in future test runs.
Step 3 — Creating a test from scratch
In this section, we will learn how to create our own tests from scratch. The
code below constitutes what we'll be testing. It defines a room that contains
multiple people. We can add a new person to the room, remove a person from the
room, and check if a person is in the room.
Create a new app/Room.php file in your editor and paste the code below into
the file:
Copied!
code app/Room.php
./app/Room.php
Copied!
<?php
namespace App;
class Room
{
/**
* @var array
*/
protected $people = [];
/**
* Constructor. Fill the room with the given people.
*
* @param array $people
*/
public function __construct($people = [])
{
$this->people = $people;
}
/**
* Check if the specified person is in the room.
*
* @param string $person
* @return bool
*/
public function has($person)
{
return in_array($person, $this->people);
}
/**
* Add a new person to the room.
*
* @param string $person
* @return array
*/
public function add($person)
{
array_push($this->people, $person);
return $this->people;
}
/**
* Remove a person from the room.
*
* @param string $person
* @return array
*/
public function remove($person)
{
if (($key = array_search($person, $this->people)) !== false) {
unset($this->people[$key]);
}
return $this->people;
}
}
Next, let's create a new unit test with the following command:
The above command creates a RoomTest.php file in the tests/Unit directory,
and populates it with some boilerplate code:
./tests/Unit/RoomTest.php
Copied!
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
class RoomTest extends TestCase
{
/**
* A basic unit test example.
*
* @return void
*/
public function test_example()
{
$this->assertTrue(true);
}
}
The assertTrue() and assertFalse() assertions
Go ahead and open the tests/Unit/RoomTest.php file that was generated and
update its contents as follows:
./tests/Unit/RoomTest.php
Copied!
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
// Import the room
use App\Room;
class RoomTest extends TestCase
{
/**
* Test the has() method in Room class
*
* @return void
*/
public function test_room_has()
{
$room = new Room(["Jack", "Peter", "Amy"]); // Create a new room
We've already seen the assertTrue() method in step 2. The assertFalse()
method works in the same manner except that it expects a false value. In this
example, "Jack" is in the room, and "Eric" is not, so $room->has("Jack")
returns true and $room->has("Eric") returns false, causing both assertions
to pass.
You can try it out by running the command below. Notice that we can choose to
run a specific test file by specifying the path to the file like this:
Copied!
./vendor/bin/phpunit tests/Unit/RoomTest.php
Output
Time: 00:00.012, Memory: 10.00 MB
OK (1 tests, 2 assertions)
The assertContains() assertion
Let's write another test to verify the behavior of the add() method. Instead
of assertTrue() or assertFalse(), we'll go a different way. We will add a
new person to the room, then check if the room contains that person using the
assertContains() method.
Add the following function to the RoomTest class beneath the test_room_has()
method:
./tests/Unit/RoomTest.php
Copied!
. . .
/**
* Test the add() method in Room class
*
* @return void
*/
public function test_room_add()
{
$room = new Room(["Jack"]); // Create a new room
The assertContains() assertion takes two parameters: the first one is the
value we expect the array, which is the second parameter, to contain. In our
example, a new room that contains only Jack is created, and then
$room->add("Peter") adds Peter to the room. Finally, we use
assertContains() to test if Peter is in the room.
Run the test with the following command:
Copied!
./vendor/bin/phpunit tests/Unit/RoomTest.php
You should observe that the test passes:
Output
Time: 00:00.007, Memory: 8.00 MB
OK (2 tests, 3 assertions)
Laravel also offers a powerful command-line tool called artisan which we can
use to execute our tests as well. The artisan command provides a more detailed
output:
Copied!
./vendor/bin/sail php artisan test
Output
PASS Tests\Unit\ExampleTest
✓ that true is true
✓ that name is jack
PASS Tests\Unit\RoomTest
✓ room has
✓ room add
Tests: 4 passed
Time: 1.47s
With the help of the artisan command, it is possible for us to check how much
of our code is covered by the tests. First, we need to install a PHP extension
called Xdebug by following
these instructions.
Once Xdebug is installed, we need to add a new environment variable in the
.env file to set its coverage mode:
After saving the .env file, you need to stop and restart Laravel Sail by
pressing Ctrl-C in the terminal and executing ./vendor/bin/sail up once
more. Now, we can run the artisan test command with the --coverage flag to
see how much of our code is covered by the tests:
As you can see, the code coverage percentage for each file is listed below the
test results, and we have a 48.4% overall test coverage for our application.
The assertCount() assertion
Our final test for the Room class involves testing the remove() method with
assertCount(). This method works similarly to assertContains(), except that
the first parameter must be the number of items in the array.
Add the following function below the test_room_add() method:
./tests/Unit/RoomTest.php
Copied!
/**
* Test the add() method in Room class
*
* @return void
*/
public function test_room_remove()
{
$room = new Room(["Jack", "Peter"]); // Create a new room
$this->assertCount(1, $room->remove("Peter"));
}
The function above creates a new room with two members and uses the
assertCount() method to test whether removing a member from the room reduces
its member count to 1.
Rerun the test again to view the results:
Copied!
./vendor/bin/sail php artisan test ./tests/Unit/RoomTest.php
In a real-world web application, we probably won't need to test simple arrays as
in the previous step. Instead, we'll probably be dealing with APIs, HTTP
requests, and responses. Laravel also provides an example of this type of test.
Go ahead and open the ./tests/Feature/ExampleTest.php file in your text
editor:
Copied!
code ./tests/Feature/ExampleTest.php
./tests/Feature/ExampleTest.php
Copied!
public function test_the_application_returns_a_successful_response()
{
$response = $this->get('/');
$response->assertStatus(200);
}
In this example, a GET request is sent to the root URL, and the returned
response is assigned to the $response variable. On the following line, we
check if the response has a success code of 200 with the assertStatus()
assertion.
Sometimes, an endpoint requires us to pass some query parameters. In such cases,
we can do this:
Copied!
public function test_the_application_returns_a_successful_response()
{
Just testing the response status is not enough for a real application. Usually,
we need to test the response body to ensure the data that is returned to us is
valid and matches expectations. In most cases, the response data is either in
JSON or HTML format. In the next two sections, we will discuss how to test them
both in detail.
Testing JSON data
The backend and frontend of most modern web applications are usually running
separately, and data is shared between them in JSON format. Laravel offers us
some helpers for testing that the JSON data received from an endpoint is valid
and accurate. For instance, the assertJson() method is provided to ascertain
the validity of JSON data.
Let's take a look at the following example. Open the routes/web.php file and
create a new route. We'll make this route return some JSON data when a GET
request is made to it.
Create a test for this route using the following command:
Copied!
./vendor/bin/sail php artisan make:test JSONTest
Output
Test created successfully.
Open the newly created ./tests/Feature/JSONTest.php and update its contents as
follows:
./tests/Feature/JSONTest.php
Copied!
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class JSONTest extends TestCase
{
/**
* A basic feature test example.
*
* @return void
*/
public function test_json()
{
$response = $this->get('/json-test');
$response->assertStatus(200);
$response->assertJson([
'updated' => true,
]);
}
}
In this test, we're making a GET request to the /json-test route and assigning
its response to the $response variable. Next, we test if the response status
is 200, and if the response data contains the value 'updated' => true.
If you run this test now, both assertions should pass:
Copied!
./vendor/bin/sail php artisan test ./tests/Feature/JSONTest.php
The test above only checks that the JSON response contains a property. In cases
where you want the JSON data to be an exact match, you can use the
assertExactJson() assertion like this:
Copied!
. . .
public function test_json()
{
$response = $this->get('/json-test');
$response->assertStatus(200);
$response->assertExactJson([
'updated' => true,
]);
}
. . .
After making the above changes, the test will fail since the provided JSON is
not an exact match with the response.
Copied!
./vendor/bin/sail php artisan test ./tests/Feature/JSONTest.php
Sometimes, we expect a route to return some HTML instead of JSON. Laravel is
also able to test HTML responses for us so that we can verify that the route is
working correctly.
Before we write a new test, let's create the view and router we'll be testing.
Head over to the ./resources/views directory and create a new file called
test.blade.php.
Next, let's create a new test class for the view we just created, and we'll call
it ViewTest.php:
Copied!
./vendor/bin/sail php artisan make:test ViewTest
Output
Test created successfully.
Open the ./tests/Feature/ViewTest.php file and create the following test:
Copied!
code ./tests/Feature/ViewTest.php
./tests/Feature/ViewTest.php
Copied!
. . .
class ViewTest extends TestCase
{
/**
* Test if the returned HTML page contains the name Taylor.
*
* @return void
*/
public function test_if_view_contains_Taylor()
{
$response = $this->get('/view-test');
$response->assertStatus(200);
$response->assertSee('Taylor');
$response->assertSee('<p>The name is Taylor.</p>', false);
}
}
In this example, we first visit the URL /view-test, and assign the response to
the variable $response. Then we use the assertStatus() assertion to verify
that the response has a success code of 200.
On the following line, we used the assertSee() method to test if the returned
HTML page contains the word Taylor. This assertion will automatically escape
the given string unless you pass false as the second parameter, as seen in the
third assertion. If allow string escaping, <p> will be treated as <p>
and not as an HTML tag.
Run the test with the following command:
Copied!
./vendor/bin/sail php artisan test ./tests/Feature/ViewTest.php
Output
PASS Tests\Feature\ViewTest
✓ if view contains taylor
Tests: 1 passed
Time: 0.72s
So far, we've discussed how to write basic unit tests, and then we demonstrated
some strategies for testing HTTP responses in real-world applications. Let's
take it one step further by testing code that reads from or writes to a
database.
We know that for most applications, we need to store information inside a
database, so it is very important ensure that the database is working as
designed before we deploy our application into production. To do this, the first
step is generating some fake data inside our database.
Generating fake data
Laravel Sail already took care of the database configurations for us when we ran
the ./vendor/bin/sail up command for the first time. By default, Laravel
provides us with a User model (./app/Models/User.php) and the corresponding
migration file
(./database/migrations/2014_10_12_000000_create_users_table.php). In this
section, we will use this User model to demonstrate how to test databases in
Laravel.
Go ahead and run the migrations directly. Remember that we need to run the
command within Laravel Sail:
Right now, the database is still empty, but we can use a factory to generate
some data for testing. Here's the artisan command for generating a new
factory:
However, Laravel provides us with an example for the User model. Open the
./database/factories/UserFactory.php file in your text editor and examine its
contents:
Copied!
code ./database/factories/UserFactory.php
./database/factories/UserFactory.php
Copied!
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
*/
class UserFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition()
{
return [
The factory file tells Laravel what type of data should fill the corresponding
field. In this example, the name and email fields are generated using the
PHP's Faker library, email_verified_at is
set to the current time, password is a hard-coded string, and remember_token
is generated randomly as a string containing 10 characters.
Running database tests
To use this factory to generate test data, we must ensure that it is tied to the
User model. Open file ./app/Models/User.php and make sure the User model
is using HasFactory:
./app/Models/User.php
Copied!
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
. . .
}
Laravel will automatically go to ./database/factories and look for a class
name matching the model name with a suffix Factory
(<model_name>Factory.php).
Next, it is time for us to generate a test for the user database table. Run
the following command in your terminal:
Open ./tests/Feature/DatabaseTest.php in your text editor and create a new
test for the user database:
Copied!
code ./tests/Feature/DatabaseTest.php
./tests/Feature/DatabaseTest.php
Copied!
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
use App\Models\User;
class DatabaseTest extends TestCase
{
use RefreshDatabase;
/**
* Test the user database.
*
* @return void
*/
public function test_user_database()
{
User::factory()->count(3)->create();
$this->assertDatabaseCount('users', 3);
}
}
In this example, we used RefreshDatabase to make sure after each test, the
database is reset to its original state. You can comment this line out if you
don't think this is necessary. We must also import the User model into the
test file.
Inside the test_user_database() function, we inserted three records into the
users table through the user factory we just created. Then we used the
assertDatabaseCount() assertion to verify that there are three records in the
users table.
Of course, there are many more useful assertions available to us. For instance,
assertDatabaseHas() test the existence of a specific record in the table and
assertDatabaseMissing() verifies the lack of a record.
Finally, we can run the database test using the following command, and verify
that it passes:
Copied!
./vendor/bin/sail php artisan test tests/Feature/DatabaseTest.php
Output
PASS Tests\Feature\DatabaseTest
✓ user database
Tests: 1 passed
Time: 1.44s
Step 6 — Testing in the browser with Laravel Dusk
Besides unit testing and feature testing, there is also browser testing which
can be useful in certain situations. It allows us to simulate the user's
behavior inside a browser, such as typing something in a form or pressing a
button. To do this, we need to use a package called Laravel Dusk.
Installing Laravel Dusk
Go ahead and download the Laravel Dusk package using Composer:
Using version ^6.24 for laravel/dusk
./composer.json has been updated
Running composer update laravel/dusk
. . .
Discovered Package: laravel/dusk
Discovered Package: laravel/sail
Discovered Package: laravel/sanctum
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Discovered Package: spatie/laravel-ignition
. . .
Publishing complete.
Laravel Dusk is dependent on
Google Chrome and the
ChromeDriver, and if you
followed this tutorial from the beginning and used Laravel Sail to initialize
our project, the ChromeDriver should already be installed when we create the
Docker container. You can verify this by opening the docker-compose.yml file
in your project and check if the following code exists:
This setup will ensure that all the necessary components are installed when you
run the ./vendor/bin/sail up command. You do still need to install Google
Chrome manually though, if you haven't done so already.
When you're ready to proceed, execute the browser test through the following
artisan command. Make sure Laravel Sail is running in the background before
doing so.
Copied!
./vendor/bin/sail dusk
Output
Time: 00:02.588, Memory: 22.00 MB
OK (1 test, 1 assertion)
If you didn't follow this tutorial from the start, and you are running the
Laravel project without Docker, you need to install ChromeDriver with the
duck:install command.
Copied!
php artisan dusk:install
Output
Dusk scaffolding installed successfully.
Downloading ChromeDriver binaries...
ChromeDriver binaries successfully installed for version 101.0.4951.41.
To run the browser test without Laravel Sail, use the following command, and the
output will be the same as before:
Copied!
php artisan dusk
Writing browser tests with Laravel Dusk
You should find a Browser directory under the tests folder, and inside
Browser, an ExampleTest.php file has been provided for us so let's take a
closer look:
./tests/Browser/ExampleTest.php
Copied!
<?php
namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
public function testBasicExample()
{
$this->browse(function (Browser $browser) {
$browser->visit('/')->assertSee('Laravel');
});
}
}
This file looks very similar to a unit or feature test, except that we are using
the browse() method to simulate an actual browser. The root URL is visited and
then the assertSee() method is used to check if the word Laravel exists in
the resulting web page.
We can also do something more challenging with Laravel Dusk. In the following
example, the /login route is visited and the form on the page is filled.
Laravel will find the input field with the name attribute set to email and
type in 'example@test.com', and then do the same for the password field
before finally pressing the Login button to submit the form.
Laravel Dusk provides us with tons of other methods to interact with your web
page but I can't list them all here. If you are interested in exploring further,
consider reading
their documentation pages.
Step 7 — Mocking external APIs in Laravel
In this section of the tutorial, we will discuss the concept of mocking and how
to do it in a Laravel application. Sometimes, when we are testing our code, we
wish to prevent a portion of the code from executing to keep the test fast and
prevent side effects. For example, when we are testing code that interacts with
an API, we generally don't want to make a request to the actual API to prevent
the test from being flaky due to network conditions and service availability. We
can mock that API response with the fake() method in such cases.
Let's start by creating a new test:
Copied!
./vendor/bin/sail php artisan make:test MockTest
Output
Test created successfully.
Next, add the test_mock_http() test to the MockTest.php file that we just
created:
./tests/Feature/MockTest.php
Copied!
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
use Illuminate\Support\Facades\Http;
class MockTest extends TestCase
{
/**
* A basic feature test example.
*
* @return void
*/
public function test_mock_http()
{
// This api is supposed to return a list of countries in JSON format,
Notice that we are using Laravel's Httpfacade to make HTTP
requests here and this example API is supposed to return a list of countries in
JSON format. In the above test, we are forcing it to return custom JSON data and
a 200 response code, and we're using the assertJsonStringEqualsJsonString()
method to verify if that is true.
Run this test with the following command:
Copied!
./vendor/bin/sail php artisan test ./tests/Feature/MockTest.php
You can use this technique to test any piece of code that interacts with some
API to avoid making network requests in your unit tests.
Step 8 — Testing your Laravel application with GitHub Actions
In the final section of the tutorial, I will demonstrate how to set up a
Continuous Integration pipeline for testing your code with
GitHub Actions. Ensure that you have
Git installed on your system before proceeding.
First, open a new terminal and initialize a git repository in the root of your
project through the following command:
Copied!
git init -b main
Output
Initialized empty Git repository in /<path_to_your_project_root_directory>/.git/
Commit all files in the directory to the local git repository:
Next, we need to push this local repository to GitHub. Create a new repository
for your project on GitHub:
Copy the remote URL to this repository
Head back to the terminal, and push your changes to the GitHub remote we just
created using the following commands:
Copied!
git remote add origin <remote_url>
Copied!
git branch -M main
Copied!
git push -u origin main
Output
Enumerating objects: 108, done.
Counting objects: 100% (108/108), done.
Delta compression using up to 10 threads
Compressing objects: 100% (89/89), done.
Writing objects: 100% (107/107), 71.63 KiB | 6.51 MiB/s, done.
Total 107 (delta 7), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (7/7), done.
To https://github.com/ericnanhu/git-test.git
eb84310..1e1c3e1 main → main
Branch 'main' set up to track remote branch 'main' from 'origin'.
Next, in order to create a CI pipeline, we need to create a .github/workflows
directory under the root directory of our project. GitHub Actions will
automatically look for workflow files in this directory.
Copied!
mkdir -p .github/workflows
Create a test.yml file inside the .github/workflows directory and open it up
in your text editor:
This yml file essentially defines a series of commands that runs every time we
push or make a pull request to the main branch. Once such an event is
detected, the commands listed in the jobs.laravel-tests.steps section will be
executed in order by GitHub. The runs-on property specifies that the testing
environment will be the latest version of Ubuntu, and all the commands within
steps have a name and a run property that defines the command to run.
The first step installs PHP 8.1 in the testing environment and the second checks
out the GitHub repo so that the workflow can access it. Next, the .env.example
file is copied into the .env file if it doesn't exist already, and composer
is updated and used to install the necessary dependencies. The
php artisan key:generate command is used to set the APP_KEY value in your
.env file, and the necessary directory and database configurations are performed
in the next two steps. Finally, the tests are executed through phpunit.
Go ahead and make some changes to your project, then commit and push them to the
main branch, it will automatically trigger this pipeline. If nothing goes
wrong, all jobs should be successful. You can see the result by visiting the
Actions tab of your GitHub repository.
Conclusion
Testing your code is a non-negotiable step in the software development life
cycle. When you are creating a Laravel application, it is best to write
feature/unit tests for every controller, database tests for every database
table, and browser tests for every web page. This process guarantees the
integrity of your code.
In this tutorial, we've talked about the basics of unit testing in Laravel.
We've studied how to set up different testing environments in the PHPUnit
configuration file phpunit.xml, how to create and run tests using terminal
commands, and how to test code through assertions.
We also discussed how to test HTTP responses which is more practical for
real-world web applications, and we talked about database testing and how to
generate fake data for testing purposes. Finally, we briefly introduced browser
testing which simulates real users in a real browser, and the concept of
mocking, which allows us to override the functionality of a piece of code so
that we could test other parts of our application.
Please note that this tutorial does not include everything on testing in
Laravel, it merely opens the door for you to get started and learn more. You can
read more about testing in
Laravel's official documentation.
Eric is a technical writer with a passion for writing and coding, mainly in Python and PHP. He loves transforming complex technical concepts into accessible content, solidifying understanding while sharing his own perspective. He wishes his content can be of assistance to as many people as possible.
Are you a developer and love writing and sharing your knowledge with the world? Join our guest
writing program and get paid for writing amazing technical guides. We'll get them to the right
readers that will appreciate them.