Contact Us

Getting started with TDD in Laravel with CRUD Example – 5 Balloons

Mobile App | October 7, 2020

As I am learning more and more about TDD development. I am getting fascinated with this approach of development and the enormous benefits that it brings for the application development.

So I decided to write a tutorial on how to approach TDD with a very simple CRUD application in Laravel.

For those of you unaware of these terms, here is what it means.

TDD Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: first the developer writes an (initially failing) automated test case that defines a desired improvement or new function, then produces the minimum amount of code to pass that.

CRUD The CRUD cycle describes the elemental functions of a persistent database. CRUD stands for Create, Read, Update and Delete. (Retrieve may occasionally be substituted for Read.) These functions are also descriptive of the data life cycle.

So, the TDD approach we are going to follow is very simple.

We are going implement a very basic To-Do application as an example for this tutorial. In this application, you can create, read, update and delete the Tasks.

If you are following along, make sure you have following ready.

Alright, Let’s dig into the steps.

Table of Contents

#1 Setup Model and Migrations

Let’s start thinking here of what our To-Do application will consist of, Since for this example we are considering a very basic To-Do, we can make it very simple with just two Model i.e. Tasks and Users.

And we can think of this relationship that exists between them.

A task is created by users.

A real life application will have many more models but we are going to go with these two for this example.

We already have a User model available by default in our application at app directory. Let’s start by generating the authentication scaffolding.

This will generate the basic layout file and also code for user authentication (login, logout, registration, forget password etc.)

Next, let’s generate model and controller for our Task model.

This will generate a new Model file named Task.php in our app directory, also we have appended the command with attributes -mr , this denotes that we also want database migration file to be generated and also a resourceful controller (Controller with default CRUD methods)

Let’s now go ahead and modify the database migration file for our Task database table.

Go to your code editor and navigate to folder database > migrations, you will find a migration file with name create_tasks_table in this folder. This file was generated as part of make:model artisan command.

It’s now a good time to think of what data we want to store in our tasks table. For this simple example. Tasks will have a title and a description and also each and every task is associated with a user.

Modify the up() method of the migration file like below.

 

Make sure you have your project connected to database. Run the following artisan command to migrate the database tables.

#2 Generate Model Factory

We are now ready to generate model factory for our database table. Model Factory will be used to seed our table with test data. We will also make use of Model Factory in our TDD tests.

Model Factory are stored at directory database > factories .  By default, we already have a model factory for User model with name UserFactory.php

Let’s now generate Model Factory for our Tasks table.

Run the following command on your terminal at project root directory.

We will make use of Faker library to generate dummy content for the title and description of tasks table.

We can now test our model factory. Run php artisan tinker in your terminal / command-line.

We can seed data into our database using factory method.

This will create 20 rows in your tasks table, and since we have associated user_id to with a new user. This will also create 20 users in the process.

#3 Getting Started with Test Driven Development

Let’s get started with TDD, We will create our tests in folder named tests in the application root directory. This consist of two folders by default Feature and Unit. And there is already a test named ExampleTest.php in both the folders. We can remove this both tests.

Before we start creating a new test.

We don’t want to work with our local database, since that might be already populated with other data. For the tests we should be able to whip up exactly what is needed for our test requirements.

Thus we will use sqlite memory database.

Let’s make some configuration changes to the phpunit.xml file, which is located at the root folder of your laravel application. and set the environment variable as given below

This tells out phpunit to use sqlite and not to use actual database, just do everything in memory.

Sweet!

#4 A user can read all the tasks [READ]

Let’s create our First Test. Go to your tests > Feature directory and create a new file with name TasksTest.php

This is the skeleton of our new Test Class. As you notice that we are importing DatabaseMigration trait in our test. This denotes that for every test we will migrate the database if required and once the test is completed we will roll it back (undo the migrations). Thus for every test we will have a fresh blank database to work with.

Let’s start by adding our first feature test in the Test Class. The first feature we can think of is that, user should be able to read all the tasks in the database. Add a new test that says exactly the same.

That is our first test, we have made use of our factory method to whip-up a new task in the database and we then go to tasks url and assert that we see the task title. Very Simple first test.

Running the test

Now to run this test, go to your application terminal and run the following command.

By using –filter we can run a single Test Class or a single Test method. Of-course the test will fail, since we don’t have any code against the test.

Now, Let’s write our code to make the test form red to green.

Before anything, we need a route file that points to the tasks url. Go to your route file web.php which is located under routes folder and add a GET route for the tasks url.

Now we’ll modify the index method of TaskController to get list of tasks and pass it on to the view.

Here we make use of Task Eloquent model to get all the tasks sorted by created time and pass it on to the view.

Next, as you may have guessed, we need to create a new view file named index.blade.php under folder resources > tasks .

 

Alright, we are now all set to run our test again.

There you go, we have now our test giving out green colors, and we have not yet looked at the browser to see if it works. And since now our test is passing, we are sure it’s working in browser too.

#5 A user can read a single task [READ]

Let’s move on to our next feature i.e. a user can read a single task. Which basically means that each and every task has a unique URI path and when we visit that path we can see the tasks details.

Let’s create a new test class a_user_can_read_a_single_task, I have added comments in the method

In this test, we have created a task in our database and we assert that we see the task detail when we visit the single task page. When we run the test this will obviously fail since we don’t have the necessary code in place yet to make it work.

Alright, let’s make the necessary efforts to make this work.

First off , we know that we don’t have necessary route in our web.php file to make this request work. So, we will create a new GET route in the routes file.

Next, modify the show method in your TaskController to get the Task detail by route model binding and then pass on the task to the view.

Now we know that we need to create a new blade file show.blade.php in our resources > views > tasks folder.

 

We now have all the required code in place, Run the test again.

And now we get green, Let’s move on to another feature.

#6 An authenticated user can create new task [CREATE]

Let’s move on to the Create part of the CRUD process. Next up , let’s create a test where we assert that an authenticated user can create new task in the database.

This is the skeleton of our new test method, read the comments in the method to understand what we are trying to achieve. Let’s go ahead and write the test itself.

 

Run the test, and it will give you red color. We don’t have the end-point to create a new task yet.

Let’s dive in to to make this test into green.

First, we need to create an endpoint to create a new task. Add a new route to the web.php route file.

NOTE: Make sure to put this route before your entry for route /task/{tasks} , because if you put this after this entry. Wildcard matching will consider ‘create’ as parameter for show task url.

Modify the store method in TaskController to add logic to add new task into the database.

Run the test and it will still fail, because we haven’t yet gaurded our properties in the Task model. Add this line to your Task model.

Run the test again, and you should get green !

While we are at it, we need to make sure that unauthenticated users should not be able to hit the endpoint to create a new task. Let’s add another test to verify this.

Here we are asserting that when an unathenticated user tries to hit endpoint he should be redirected to the login page. Run the test and it will fail with this error

Caused by
PDOException: SQLSTATE[23000]: Integrity constraint violation: 19 tasks.user_id may not be NULL

This means that unathenticated can still hit the endpoint. We need to fix this.

Go to your TaskController.php file and modify the contruct method to apply auth middleware.

We have applied auth middleware to all the methods of TaskController except index and show.

Run the test again, and you should get green.

Now that we have the endpoint API ready. That means we can now go ahead and create the front-end ‘form page’ to submit data to this API.

Add a new route to your web.php routes file.

Go to your TaskController.php and modify the create method to return the view file to create a new task.

Next up we need to add the view file named create.blade.php in folder resources > views > tasks

Make sure your create task view files has following.

We already have our endpoint ready and tested. When you submit details from your create task form it should just work.

Testing Validation.

We have our Create New Task form page and the endpoint ready. But we have not handled the validation yet. If you submit your form without entering any data it will post null data to the endpoint and we will get an error from the database regarding the NULL value.

Let’s fix that. We will add a new tests to assert the validation.

Here we have added two new test to verify that we are validating both the fields title and description. Also we specifically passed on a null value to the endpoint and then we are asserting that the session has error with the specified field name in it.

Run the test and it will fail. Since we don’t have validation in place yet.

Let’s add code to validate the form fields. Modify the store method of TaskController

Run the test again and it should pass this time.
task requires description test pass

We also need to handle this on your front-end to show error messages to the user. Add this code below your form to show error messages

And now we have the validation working.

We are making good progress ! Let’s move on to the next part.

#7 Authorized user can update the task [UPDATE]

Let’s move on to the Update part of our CRUD application. An authorized user should be able to update his tasks.

Add a new test in TasksTest test class.

Here we have persisted a task in the database and then later we modify the title of the task and make a put request to /task/$task->id url. After that we assert that the title has been updated in the database.

Let’s run the test, and it will fail since we don’t have the API endpoint ready yet to update the task.

Let’s create the update task endpoint. Add the following entry into the web.php routes file.

We now need to modify the update method of the TaskController class.

Run the test again and it should give you green

Next, we need to make sure that unauthorized user’s should not be able to edit/update the task details. Think about that in a way that you created  task but now any authenticated user can edit it. We need to prevent this

Let’s add a new test for this named  unauthorized_user_cannot_update_the_task

Here we sign-in as a user and try to hit the update endpoint for a task which is not created by the user. In such case we expect to get UnAuthorized Error or a 403 response status.

Run the test and it will fail, since we don’t have code yet to prevent this.

Let’s fix this

We need to create a Policy which will check if the user is authorized to perform certain actions, Go to your terminal / command-prompt and run the below artisan command

This artisan command will create a new File named TaskPolicy in folder app > Policies

Open TaskPolicy.php and modify the update method

We have denoted that for update action the tasks user_id should be same as user’s id. Which makes sense, user should only be able to perform edit action on the task that which he owns.

Next, we need to register this policy in AuthServiceProvider.php which is located in directory app > Providers

Update the policies array variable as below

Next modify the update method to include authorize code in place

Run the test again and this time it should pass


Let’s move to the front end part of update task. Add a new route to the web.php routes file

Modify the edit method of TaskController

Add a new view file to show the edit task form named edit.blade.php in resources > views > tasks folder.

We have used {{method_field('PUT'}} directive in our form to denote that we want to make a PUT request to the action URL.

Next let’s add an edit button to show.blade.php page.

We have added a bootstrap card-footer for the edit button

We are making use of @can blade directive. Which means that the Edit button will only be shown if the user has update authorization.

We are alomst there, Let’s move on to the next and last part of CRUD.

#8 Authorized user can delete the task [DELETE]

Authorized user should also be able to delete their tasks, Let’s add a new test for this named authorized_user_can_delete_the_task

Here, we try to make a delete request to the url and then we assert that the database table tasks no longer have the task.

Run the test and it will give you red.

Let’s make necessary code changes to take the test from red to green.

Add a new entry to route file web.php

Next, modify the destroy method of TaskController to handle the delete request.

Run the test again and it should now pass.

While we are at it, let’s add another test that asserts that unauthorized user should not be able to delete tasks.

 

This test should pass, since we have already added the authorize method in the destroy method.

Now since we have our tests passing, let’s move to the front-end part of this. Add the following snippet to show.blade.php

This has the form with button to delete the task, put this snippet inside @can directive so that it is only visible to user who are authorized to delete the task.

Run Full Test Suite

We are done with the CRUD implementation in Laravel with TDD. It’s good idea to run the full test suite to check that everything is running as expected and we did not broke anything in our progress.

Run the full suite

Everything should pass

Great ! We wrote 10 tests and made 14 assertions.

Hope you had fun learning the basics of TDD with Laravel and you also realized the benefits it brings to your development process.

You might find related articles useful

This content was originally published here.