Hướng dẫn viết unitest với laravel

  • December 24, 2022
  • 920

Laravel sử dụng phpunit giúp test ứng dụng dễ dàng, framework đã tích hợp sẵn chúng ta có thể config mở rộng hoặc sử dụng luôn. Thêm các config cho phpunit test trong file phpunit.xml. Các file test được tạo trong thư mục tests, có 2 loại test: feature test và unit test

Create test with laravel

Tạo 1 feature test, các file test được tạo trong folder tests/Feature


php artisan make:test UserTest

// Create UserController in tests/Feature/Controllers/Api/V1
php artisan make:test Controllers/Api/V1/UserControllerTest

Tạo 1 unit test, cái file test được tạo trong folder tests/Unit


php artisan make:test UserTest --unit

// Create UserController in tests/Unit/Controllers/Api/V1
php artisan make:test Controllers/Api/V1/UserControllerTest --unit

How to write test with laravel

Tạo testTrait viết các function dùng chung


<?php

namespace Tests\Traits;

use App\Models\User;

trait TestTrait
{
    /**
     * Create user
     *
     * @return User
     */
    protected function createUser(): User
    {
        return User::factory()->create();
    }

    /**
     * Generate Token
     *
     * @param User|null $user User
     * @return string
     */
    protected function generateToken(User $user = null): string
    {
        if (!$user) {
            $user = $this->createUser();
        }

        return $user->createToken('token')->plainTextToken;
    }

    /**
     * Handle headers with bearer token
     *
     * @param User|null $user User
     * @return array
     */
    protected function headersWithToken(User $user = null): array
    {
        return [
            'Authorization' => 'Bearer ' . $this->generateToken($user),
        ];
    }
}

Tạo test cho userController


<?php

namespace Tests\Feature\Controllers\Api\V1;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Symfony\Component\HttpFoundation\Response;
use Tests\TestCase;
use Tests\Traits\TestTrait;

class UserControllerTest extends TestCase
{
    use RefreshDatabase;
    use TestTrait;

    /**
     * Test get list users success.
     *
     * @return void
     */
    public function testGetUsersSuccess()
    {
        $response = $this->get('api/v1/users', $this->headersWithToken());
        $response->assertStatus(Response::HTTP_OK);
        $response->assertJson([
            'status' => true,
        ]);
        $response->assertJsonStructure([
            'status',
            'message',
            'data',
        ]);
    }

    /**
     * Test get list users paginate success.
     *
     * @return void
     */
    public function testGetUsersPaginateSuccess()
    {
        $response = $this->get('api/v1/users/paginate', $this->headersWithToken());
        $response->assertStatus(Response::HTTP_OK);
        $response->assertJson([
            'status' => true,
        ]);
        $response->assertJsonStructure([
            'data',
            'status',
            'pagination',
            'message',
        ]);
    }

    /**
     * Test get list users paginate error.
     *
     * @return void
     */
    public function testGetUsersPaginateInvalid()
    {
        $response = $this->get('api/v1/users/paginate?page=abc', $this->headersWithToken());
        $response->assertStatus(Response::HTTP_UNPROCESSABLE_ENTITY);
    }

    /**
     * Test get detail user.
     *
     * @return void
     */
    public function testGetUserSuccess()
    {
        $user = $this->createUser();
        $response = $this->get('api/v1/users/' . $user->id, $this->headersWithToken());
        $response->assertStatus(Response::HTTP_OK);
        $response->assertJson([
            'status' => true,
        ]);
        $response->assertJsonStructure([
            'status',
            'message',
            'data',
        ]);
    }

    /**
     * Test update user.
     *
     * @return void
     */
    public function testUpdateUserSuccess()
    {
        $user = $this->createUser();
        $params = [
            'name' => 'test',
        ];
        $response = $this->put('api/v1/users/' . $user->id, $params, $this->headersWithToken());
        $response->assertStatus(Response::HTTP_OK);
        $response->assertJson([
            'status' => true,
        ]);
        $response->assertJsonStructure([
            'status',
            'message',
            'data',
        ]);
        $this->assertDatabaseHas('users', [
            'name' => 'test',
        ]);
    }

    /**
     * Test update user.
     *
     * @return void
     */
    public function testDeleteUserSuccess()
    {
        $user = $this->createUser();
        $response = $this->delete('api/v1/users/' . $user->id, [], $this->headersWithToken());
        $response->assertStatus(Response::HTTP_OK);
        $response->assertJson([
            'status' => true,
        ]);
        $response->assertJsonStructure([
            'status',
            'message',
            'data',
        ]);
    }
}

Chú ý sử dụng các trait DatabaseTransactions RefreshDatabase để clear dữ liệu trong database


// Truncate database
// Xoá tất cả dữ liệu trong database khi run test
use Illuminate\Foundation\Testing\RefreshDatabase;

// Database transactions
// Chỉ xoá dữ liệu do chạy test tạo ra
use Illuminate\Foundation\Testing\DatabaseTransactions;

Run command test with laravel

Command chạy unitest trong laravel


// Test all
php artisan test

// Test use config in env.testing
php artisan test --env=testing

// Test for special file
php artisan test --filter=UserControllerTest

// Test for special function
php artisan test --filter=testGetUsersSuccess

Config test coverage

Sử dụng test coverage để check độ bao phủ của test, giúp chúng ta có thể check được mình đã viết đủ cảc case test chưa. Để test được coverage cần config Xdebug


// Install Xdebug in dockerFile
RUN pecl install xdebug && \
    docker-php-ext-enable xdebug

// Config php trong file php.ini
[XDebug]
zend_extension=xdebug.so
xdebug.mode=coverage
xdebug.start_with_request=no

Config test coverage trong file phpunit.xml
Config php unitest

Chạy command check độ bao phủ của test


php artisan test --coverage

// Test chỉ pass khi các file test có độ bao phủ test lớn hơn 80% 
php artisan test --coverage --min=80

Tổng kết

Như vậy chúng ta đã đã config, viết xong unitest, check được độ bao phủ của test. Giờ đến lượt bạn thử viết test với laravel cho ứng dụng của mình.Thanks for reading...