DEV Community

Cover image for TDD in Laravel using phpunit for REST Api Development
Chandresh Singh
Chandresh Singh

Posted on

TDD in Laravel using phpunit for REST Api Development

Get started with TDD in Laravel using phpunit for REST Api Development. As an example, I will be creating an api using TDD approach to allow user to update his profile.

You can also watch the entire example here:

The idea behind TDD approach is to first make the test case before writing the actual code. So writing api code becomes easy once you have already figured out all the thing you will be doing in api while writing the test case.
Once you have a test case in place then after each change in API you can validate if your api is working as expected by running the test case.

If you look into phpunit.xml. You will see environment variables defined like this.

    <php>
        <server name="APP_ENV" value="testing"/>
        <server name="BCRYPT_ROUNDS" value="4"/>
        <server name="CACHE_DRIVER" value="array"/>
        <server name="DB_CONNECTION" value="sqlite"/>
        <server name="DB_DATABASE" value=":memory:"/>
        <server name="MAIL_MAILER" value="array"/>
        <server name="QUEUE_CONNECTION" value="sync"/>
        <server name="SESSION_DRIVER" value="array"/>
    </php>

These environment variable are used while running test cases. Laravel will automatically set the environment variables defined in this file.

One thing to note here is that We are using in memory database of sqlite. The advantage of using memory database is that it is created automatically in memory when you run your tests and are automatically deleted after your test cases are completed.

It will not affect your actual database in use.

You can configure these environment variables as per your requirement.

Open TestCase file. We will have to make some changes here to migrate and seed database before test case. For this override setUp function like this.

<?php

namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Illuminate\Foundation\Testing\WithFaker;

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication, WithFaker;

    protected function setUp(): void {
        parent::setUp();

        $this->artisan('migrate');
        $this->artisan('db:seed');

        $this->withoutExceptionHandling(); //To get the actual Exception whenever it occurs instead of Laravel handing the exception.

    }
}

Let's try to create a new test using command:

php artisan make:test UserTest

Open tests/Feature/UsersTest.php

Write your first test case like this:

<?php

namespace Tests\Feature;

use App\User;
use Tests\TestCase;
use JWTAuth;

class UsersTest extends TestCase
{
    /** @test */
    public function a_user_can_edit_his_profile() {
        $user = User::first();

        $token = JWTAuth::fromUser($user);

        $attributes = ['name' => $this->faker->name];

        $this->patchJson('api/user/profile', $attributes, ['authorization' => "bearer $token"])
            ->assertStatus(200);

        $this->assertDatabaseHas($user->getTable(), array_merge($attributes, [
            'id' => $user->id
        ]));
    }
}

Here i'm using JWT but you can change it as per the package you are using to generate auth token.

I'm using assertDatabaseHas function and passing table name and the data which should be present to check if the profile is updated in database.

Now, if we try to run it will fail because we haven’t created the api route yet.

Let’s do that in api.php

Route::patch('user/profile','UserController@updateProfile');

Create a new controller using command:

php artisan make:controller UserController

Write updateProfile function in UserController to allow user to edit his profile like this:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth:api');
    }

    public function updateProfile() {
        $attributes = request()->validate(['name' => 'required|string']);

        auth()->user()->update($attributes);

        return response()->json(["msg" => "Profile successfully updated"]);
    }
}

Try running your test case again and your test case should pass this time.
Test Case Passed

That's it!

Hope you find this useful!

Top comments (1)

Collapse
 
samuhmatos profile image
Samuel Matos

Interesting post!
Do you know if there're some reasons to a test don't pass when running with another tests, but when is running alone pass?