A Short Intro to Test Driven Development in Laravel
Published: 2020-09-29 21:28:10

> FYI this is not a guide

# Day 6 of 100DaysOfCode

Completed HypeTracker's first and second test case for the Sneaker API using test driven development (TDD).

In this short post, I'll go over how I used [test driven development](https://en.wikipedia.org/wiki/Test-driven_development) to migrate the get all sneaker API route for project HypeTracker.

Find the project on GitHub here:

{% github justinhodev/hype-tracker %}

# TDD with Laravel

## Writing a Test

First, of course, I have to start off with a test case.

Laravel's `artisan` CLI tool aids in developer experience because you can just run `php artisan make:test SneakerApiUnitTest --unit` and end up with below (without the funtion).

```php
class SneakerApiUnitTest extends TestCase
{
    /**
     * Get All Sneakers.
     *
     * @return void
     */
    public function testGetAllSneakers()
    {
        $response = $this->json('GET', '/api/sneakers');

        $response->assertStatus(200);
    }

}
```

This is where I felt Laravel's "convention over configuration" philosophy shined as the [documentation on testing has clearly sections on how to develop and test REST API routes](https://laravel.com/docs/8.x/http-tests#testing-json-apis).

The first test is relatively simple as I will be getting all sneakers and since the database is seeded with random data, I only wanted to make sure I got something back.

Run `php artisan test` and the output should be like this:

![Building a Test Case to Fail](https://dev-to-uploads.s3.amazonaws.com/i/wmk8n4k6zdxv5np7y48v.png)

Yes it's supposed to fail, I haven't built anything yet!

## Building the API

Next, to build out a REST API with a JSON response, the minimal amount of coding requires a Model object returning JSON from the `api.php` file. However, I am going to split this functionality properly into 2 separate components: the controller and the resource collection.

### Controller

```php
// app/Http/Controllers/API/SneakerController.php
class SneakerController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return new SneakerCollection(Sneaker::all());
    }

    // ...
}
```

A controller in Laravel helps map specific HTTP routes to the various verbal actions in REST (GET, POST, PUT, DELETE). In this case the `index()` function maps to an HTTP GET request without extra parameters (GET '/api/sneakers').

### Resource Collection

```php
// app/Http/Resources/SneakerCollection.php
class SneakerCollection extends ResourceCollection
{
    /**
     * Transform the resource collection into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'total' => $this->count(),
            'data' => $this->collection,
        ];
    }
}
```

A Laravel resource is the data access layer which maps a database object to the return fields you want to retrieve (ex. removing the `create_at` field in the JSON response but have access to it in memory). Meanwhile, the resource collection is Laravel's way to modify the final output from many resources (ex. adding a `total` field as the first item like I did above).

I am splitting the functionality into its respective groupings as I already have and know the final API and this method is more scalable and easier to manage. If you were starting from scratch, I would recommend just returning JSON from the Model object straight from the API route.

Finally, bind the controller and resource collection to an API route.

```php
// routes/api.php
Route::apiResources([
    'sneakers' => SneakerController::class,
]);
```

Now rerun `php artisan test`!

![A Successful Test Case](https://dev-to-uploads.s3.amazonaws.com/i/ae1hjg0gqwvd835ul8a2.png)

That's it, a simple REST API GET route done with TDD. Of course, this isn't very detailed but its mostly showing my process and progress. I might make a more detailed guide later on once I am further along my project. Still very backlogged on these challenge posts.

In the meantime, feel free to follow me on Twitter [@justinhodev](https://twitter.com/justinhodev) to keep up with my journey!