How to Unit Test a Laravel API with the Pest Framework
Time to read: 5 minutes
Pest is a new testing PHP Framework developed by Nuno Maduro. While Pest itself is built on top of PHPUnit, the popular and widely adopted PHP testing framework, Pest aims to provide a better experience for writing tests. The philosophy is simple. Keep the TDD experience simple and elegant by providing expressive interfaces.
In this tutorial, we’ll be looking at how to get started using Pest in a Laravel project. Using the test-driven approach, we’ll be building a simple to-do application that allows you to create, edit, update and delete tasks.
Technical Requirements
- PHP version 7.3 or higher. Pest requires PHP 7.3+ to work.
- Laravel 8.
- Composer.
- A basic understanding of PHPUnit.
- A basic understanding of SQLite. We’ll be making use of SQLite because it makes running our tests faster.
Set up Laravel
There are different ways to set up a new Laravel project. You can do so via the Laravel installer or by using Composer. For the sake of this tutorial, we’ll be using Composer.
Run the following command in your terminal:
This will set up a new Laravel project for us in the pest-todo
directory.
Install Pest
Now that we’ve set up a new Laravel project, there are a couple of additional steps we need to carry out to set up Pest with Laravel. Type cd pest-todo
to change into the new pest-todo
directory, and then run the following command:
Next, we’ll be installing the Pest Plugin for Laravel. To do that, run the following command:
Once the plugin has been installed, run the following command:
This will create a Pest.php file in the tests directory. The Pest.php file is autoloaded automatically and serves as an ideal place to recursively bind helper classes and traits to your tests. Laravel comes bundled with some example test files based on PHPUnit. Let’s change those tests to make use of Pest instead. Head over to the tests/Feature
directory and take a look at the ExampleTest.php
file. Here’s what it currently looks like:
To migrate this test to the corresponding Pest implementation, replace the content of the file with the following code:
Much cleaner right? We’ve reduced this ExampleTest file from about 20 lines of code to just 2 lines while testing the exact same thing and producing the same result. That is, it visits the root URL at ‘/’ and asserts that an HTTP status code of 200 is returned.
Pest provides two functions for writing tests - test()
and it()
. Both functions accept a test description as the first argument and a closure that contains the test expectations as the second argument. They share the same syntax and behavior and you’re free to use either one you find fitting. I personally prefer using it()
since they make your test cases read like a complete sentence.
Similarly, let’s make the ExampleTest.php
file located in the tests/Unit directory use Pest as well. Replace the contents of the file with the following code:
Next, use the following command to run the test suite:
All tests should be passing as seen in the image below.
Create the to-do Model, Migration and Controller
Our application is going to have a single Model called Todo
. Laravel provides a handy command for generating a Model, Migration, and Controller for an entity all at once. To do that run the following command:
A new migration file is created in the database/migrations
directory at [TODAYSDATE]_create_todos_table.php
. Next, add the following code to the up()
method within the migration file:
Each to-do task will have a name
attribute as well as a boolean completed
attribute, with a default value of false
. Next, edit the App/Models/Todo.php
file with the following code:
Here, we assign the name
and completed
attribute of the model to be mass-assignable.
Create the to-do factory
Laravel model factories provide a convenient way of seeding the database with data. This is very useful when it comes to testing. Run the following command to create a factory class for the to-do model:
This will create TodoFactory.php
for us in the database/factories
directory. Edit the definition()
method within the file to return an array similar to the one below:
The definition
method returns the default set of attribute values that should be applied when creating a model using the factory.
Configure the database
We’ll be making use of an in-memory SQLite database for testing. This will make running our tests faster. Laravel already provides support for using a SQLite database for testing. Head over to the phpunit.xml file located at the root of your project’s directory and uncomment the following lines of code:
Write the tests
Now that we’ve done all the necessary setup and configuration, we can get started with writing the tests. These tests are required to have a functioning application and will provide the corresponding implementation to make sure all the tests pass.
Run the following Pest command to create a unit test file:
This will create TodoTest.php
in the tests/Unit
directory. Replace the file’s code with the following code:
At the top of the file, the uses()
method binds the TestCase
class and the RefreshDatabase
trait to the current test file. The base TestCase
class is provided by Laravel and provides helper methods for working with the framework while testing. The RefreshDatabase
trait takes care of migrating and resetting the database after each test so that data from a previous test does not interfere with subsequent tests.
Now, let’s go over what each test is doing:
- it(‘does not create a to-do without a name field’) - Laravel provides several helpers for testing JSON APIs and their responses. Here we make use of the
postJson
helper to make aPOST
request to theapi/todos
endpoint passing in an empty array. Next, theassertStatus()
method on the returned response ensures that an HTTP status code of 422 should be returned. This test ensures that a name field will always be present on the request payload. - it(‘can create a to-do’) - This test ensures that a to-do is created on making a
POST
request to theapi/todos
endpoint. We assert that an HTTP status code of 201 is returned and that the database actually contains the to-do using theassertDatabase()
method. - it(‘can fetch a to-do’) - This test checks to see that a particular to-do task can be fetched using the ID. Using the
create()
method on the Todo Factory, a to-do task is created and stored in the database. Similarly, we assert that the status code returned is 200. TheassertJson()
method converts the response to an array and verifies that the given array exists within the JSON response that will be returned by the application. - it(‘can update a to-do’) - This test ensures that a to-do task can be updated and that the updated task can be found in the database.
- it(‘can delete a to-do’) - This test ensures that a to-do task can be deleted and verifies that the total number of tasks contained within the database is zero.
To run the test suite, run the following command:
The test suite should be failing since we have not implemented any of the features.
Build the TO-DO application
Let’s provide the corresponding implementation of the tests we’ve written so far. Head over to the TodoController.php
file in the app/Http
directory and replace the file’s code with the following code:
- The
create()
method creates a new to-do task. - The
show()
method returns a given task based on it’s ID. - The
update()
method updates a to-do task. - The
delete()
method deletes a given to-do task.
Next, add the following routes to the routes/api.php
file:
Now that we’ve provided all the corresponding implementations for the tests, we can go back to running our tests and they should all be passing now. Run the test suite again with the following command:
Conclusion
In this tutorial, we’ve seen how to go about writing unit tests for a Laravel application using the Pest testing framework. This tutorial can serve as a great guide for getting started with Pest as well as unit testing a Laravel application. The GitHub repository with the complete code for this project can be found here.
Dotun Jolaoso
Website: https://dotunj.dev/
Github: https://github.com/Dotunj
Twitter: https://twitter.com/Dotunj_
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.