DEV Community

loading...

A Short Intro to Test Driven Development in Laravel

Justin Ho
I like to tinker and (mostly) break things, either software or hardware! I value human connections in the age of software and am interested in automation. Feel free to connect with me!
・3 min read

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 to migrate the get all sneaker API route for project HypeTracker.

Find the project on GitHub here:

GitHub logo jcsho / hype-tracker

Full stack web application to read data from public APIs (Twitter, Reddit) and form visualizations

HypeTracker

Current Version Codacy Badge StyleCI License FOSSA Status


HypeTracker is a data aggregator application for social media 'impressions' on sneakers bought and sold in the aftermarket.

See my blog detailing what HypeTracker is and why I am refactoring it

Changelog / Goals for V2

  • migrate database schema see PR #4
  • migrate internal sneakers API see PR #7
  • migrate front-end see PR #17
  • deploy as demo / push as v2
  • add social media api scraping

Getting Started

Requirements

Homestead

  • PHP ^7.3
  • Composer ^1.10.13
  • Vagrant ^2.2.10 (with your choice of virtualization layer)
  • (example) VirtualBox ^6.1.14 (with Vagrant's VirtualBox provider)
$ git clone https://github.com/justinhodev/hype-tracker.git
$ cd hype-tracker

# Install Composer Dependencies
$ composer install

# Prepare config for Homestead
$ composer homestead

# Provision the VM
$ vagrant up

# SSH into Vagrant Box
$ vagrant ssh
Enter fullscreen mode Exit fullscreen mode

Docker

coming soon

Database Entity Relationship Model

ER Diagram

License

FOSSA Status




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).

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

        $response->assertStatus(200);
    }

}
Enter fullscreen mode Exit fullscreen mode

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.

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

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

// 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());
    }

    // ...
}
Enter fullscreen mode Exit fullscreen mode

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

// 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,
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

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

Now rerun php artisan test!

A Successful Test Case

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 to keep up with my journey!

Discussion (0)