What is TDD Approach in Laravel? How Does it Work?

“Code can never be clean without tests. No matter how elegant it is, no matter how readable and accessible, if it hath not tests, it be unclean. ”
― Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship

The above statement by the famous author and developer, also famously know as Uncle Bob, highlights the importance of clean code.

TDD or Test-Driven Development is used in Laravel for clean code as well as improved accuracy and is also called test-driven design. In this approach, we write code for every functionality and move on to the next only when it passes the test. So it is the simplest and cleanest code you will ever write.

How Does TDD Work?

While using TDD, you do not have to spend time searching for what went wrong as all the code you refactor will have already passed the test. This is also known as the red-green refactor cycle as after all is said we can sum up TDD in three simple steps:

  • Write code for a non-existent functionality
  • Run the test for it and you will see an error.
  • Create the code based on the error and run the test again.

Now that you have a brief idea what is TDD all about, we can go ahead and actually start with it. Following our step by step guide below can make this easier for you.

Complete the Set up – Model & Migrations

Here we will attempt to understand the working of TDD in Laravel with the example of a simple To Do List app. We will be considering a very basic app with just two users two model – Tasks and Users.  And a basic relationship between them – A task is created by users.


Since we already have a user model, we will be generating the authentication scaffolding. This can be done with the code:

php artisan make:auth

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

Now create the model and controller for our Task Model with the code:

php artisan make :model Task -mr

This generates a file named Task.php in the directory of the app. The mr in the command helps us generate a database file and a resourceful controller.

We now need to modify the database migration file for our Task database table.

For this go to your code editor and navigate to folder database > migrations. Here you will find a migration file with the name create_tasks_table . This is the file generated as a part of make:model artisan command.

At this point, we also have to think about what we store in our tasks table. Let us say that our every task will be having a title and meta description.

For this modify the up() method of the migration file like given below.

public function up()

{

    Schema::create(‘tasks’, function (Blueprint $table) {

        $table->increments(‘id’);

        $table->unsignedInteger(‘user_id’);

        $table->string(‘title’);

        $table->text(‘description’);

        $table->timestamps();

    });

}

Now all you need to do is check that your project is connected to the database and then run the following code to migrate the database.

Generate Model Factory

We need a Model factory to seed data in our database tables. This Model Factory is stored at directory database > factories. The best about using Laravel is that we will already have a user mode with the name UserFactory.php.

But we also need a Mode Factory for our tasks table. This can be done by running the command below on your terminal at the project root dictionary.

php artisan make:factory TaskFactory –model=Task

We will be using faker library to generate sample content for title and meta description of the tasks table.

$factory->define(App\Task::class, function (Faker $faker) {

    return [

        ‘title’ => $faker->sentence,

        ‘description’ => $faker->paragraph,

        ‘user_id’ => factory(‘App\User’)->create()->id,

    ];

});

Now run php artisan tinker in your terminal / command-line.

With this, you can easily seed data into our database using factory method using factory(‘App\Task’,20)->create();

It will add 20 rows in the tasks table after running this command. Moreover, as we associated user_id with a new user, it will also create 20 users.

Getting Started

Using Laravel is going to be easier for you as many things are already set up. In TDD we have to make surplus tests. We will make these in the folder named test in the root directory after you remove the default folders – unit and user already present there.

We then begin with the database configuration. For this you need to visit the root directory and update the phpunit.xml there. There use the code :

<php>

<php>

    <env name=”APP_ENV” value=”testing”/>

    <env name=”DB_CONNECTION” value=”sqlite”/>

    <env name=”DB_DATABASE” value=”:memory:”/>

    <env name=”BCRYPT_ROUNDS” value=”4″/>

    <env name=”CACHE_DRIVER” value=”array”/>

    <env name=”MAIL_DRIVER” value=”array”/>

    <env name=”QUEUE_CONNECTION” value=”sync”/>

    <env name=”SESSION_DRIVER” value=”array”/>

</php>

A user can read all the tasks

A to do list will only be successful when a user can read all the tasks he has created. So we will also add a feature that lets the user check out all the tasks created by him.

For this, you need to go to tests > Feature directory and create a new file with the name TasksTest.php

This will serve as a skeleton for our new test. Here be mindful that we are importing database migration trait in our tests. So while we can migrate the tests, we might also have to roll it back as and when required.

Create and run a test that says:

vendor/bin/phpunit –filter a_user_can_read_all_the_tasks

The test will inevitably fail and we need to change it from red to green.

Add a route file that takes you to the url. You can go the route file web.php by using the code:

Route::get(‘/tasks’, ‘TaskController@index’);

 There modify the index method of  TaskController. 

Pass it to the view and make use of Task Eloquent model so that all the tasks are arranged by created time. Move both of these to the views and next create a file named index.blade.php under folder resources > tasks. This should look something like:

@extends(‘layouts.app’)

@section(‘content’)

    <div class=”container”>

        <div class=”row justify-content-center”>

            <div class=”col-md-8″>

                <div class=”page-header”>

                    <h2>All Tasks</h2>

                </div>

                @foreach($tasks as $task)

                <div class=”card”>

                    <div class=”card-header”>{{$task->title}}</div>

                    <div class=”card-body”>

                        {{$task->description}}

                    </div>

                </div>

                @endforeach

            </div>

        </div>

    </div>

@endsection

Now run the test again and it will most probably go green!

A user can read a single task

Our to do list would hardly be of any use if the users can not read the task details. So we want to add a feature that lets a user read a task that he has created.

For this, create a new test class a_user_can_read_a_single_task.

/** @test */

public function a_user_can_read_single_task()

{

    //Given we have task in the database

    $task = factory(‘App\Task’)->create();

    //When user visit the task’s URI

    $response = $this->get(‘/tasks/’.$task->id);

    //He can see the task details

    $response->assertSee($task->title)

        ->assertSee($task->description);

}

Simply assert that we see the task list detail by creating a task in the database. But when you run the test for it it will likely fail as we have not created a code for that.

Resolve this by creating a necessary GET route in web.php.

Route::get(‘/tasks/{task}’,’TaskController@show’);

Then modify the show method in TaskController to get the Task detail by route model binding. After this don’t forget to pass this task to view.

/**

 * Display the specified resource.

 *

 * @param  \App\Task  $task

 * @return \Illuminate\Http\Response

 */

public function show(Task $task)

{

    return view(‘tasks.show’,compact(‘task’));

}

Thereafter create a new blade file show.blade.php in our resources > views > tasks folder.

Now when everything looks fine run the test and it will pass!

Authenticated users can create new tasks

Now comes the create part that asserts that users can create a new task in the database. To begin with this, write the following test and it will most likely fail

** @test */

public function authenticated_users_can_create_a_new_task()

{

    //Given we have an authenticated user

    //And a task object

    //When user submits post request to create task endpoint

    //It gets stored in the database

This shows the basic framework of the test, now go ahead and write the test itself like the one given below.

When you run this test, it is more likely to fail.

We need to turn it from red to green. Do this by adding a new route to the web.php file with the code:

Route::post(‘/tasks/create’,‘TaskController@store‘);

Modify the store method in TaskController. If you run the test at this point it will still fail until you add guard your properties in the Task model. This can be done with:

class Task extends Model

{

    protected $guarded = [];

Now when you run the test it will turn green!

Testing validation:

It is never recommended to skip validation. So now when we have everything ready we can go ahead with the validation. However we can not submit a form without entering any data as it will post null data to the endpoint and you will get an error regarding the null data. But we can fix this by adding new tests to assert the validation.

Run a test for this and it will fail as we don’t have the actual validation in place yet. Let us modify the store method of TaskController.

Now if you run the test again it will pass.

 

But hey! We also need to take care of the front end. For this add the below code and it will be fully functional:

Authorized Users can update the task

Now comes the updating part of the CRUD. To achieve this add a new test in TasksTest test class. It will look something like:

/** @test */

public function authorized_user_can_update_the_task(){

    //Given we have a signed in user

    $this->actingAs(factory(‘App\User’)->create());

    //And a task which is created by the user

    $task = factory(‘App\Task’)->create([‘user_id’ => Auth::id()]);

    $task->title = “Updated Title”;

    //When the user hit’s the endpoint to update the task

    $this->put(‘/tasks/’.$task->id, $task->toArray());

    //The task should be updated in the database.

    $this->assertDatabaseHas(‘tasks’,[‘id’=> $task->id , ‘title’ => ‘Updated Title’]);

}

Here first and foremost we need a task in the database. Later we can modify the title of this task and request to /task/$task->id url. Thereafter assert that this title is updated in the database.

Run the test and it will fail as we do not have any API endpoints for it.

Route::put(‘/tasks/{task}’,’TaskController@update’);

Update this in the  web.php routes file:

After this visit the task Controller and modify the update method.

/**

 * Update the specified resource in storage.

 *

 * @param  \Illuminate\Http\Request  $request

 * @param  \App\Task  $task

 * @return \Illuminate\Http\Response

 */

public function update(Request $request, Task $task)

{

    $task->update($request->all());

    return redirect(‘/tasks/’.$task->id);

}

Run the test and it will pass!

Users can delete the task

We also want to let the users delete the tasks they created.  For this let’s add add a test authorized_user_can_delete_the_task. This shall result in:

When we make a delete request for the URL we also have to assert that its specific database table does not have that task.

However, when you run the test it will be red.

To make it green we need to work on the required changes by making a new entry to route file web.php.

Route::delete(‘/tasks/{task}’,‘TaskController@destroy‘);

Then, modify the destroy method of the controller to handle the delete request.

/**

 * Remove the specified resource from storage.

 *

 * @param  \App\Task  $task

 * @return \Illuminate\Http\Response

 */

public function destroy(Task $task)

{

    $this->authorize(‘update’, $task);

    $task->delete();

    return redirect(“/tasks”);

}

Let’s also ensure that only the authorized users are able to delete the tasks. Our work here is to prevent unauthorized users from deleting our to do lists by using:

After this run the test again and it will pass!

Once the test passes we can also move to the front-end part. Use the following code in @can directive and this option will only be visible to the authorized users.

Run the Full Suite TestCongratulation! That’s it!  Run the test again, this time to check the full site to check for any bugs or errors. Run the full suit test using the code:

vendor/bin/phpunit

Since we have carefully followed every step, the test will pass!

 

We hope now you will be able to rock the TDD in Laravel with this simple and easy process. However if  there is something you still need help with, reach out to us and talk to your experts!