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.
That's it!
Hope you find this useful!
Top comments (1)
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?