🎭

Mocking & Fakes

Laravel Fakes

<?php
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Storage;

class OrderTest extends TestCase
{
    public function test_order_sends_confirmation_email(): void
    {
        Mail::fake();

        $order = Order::factory()->create();
        $order->complete();

        Mail::assertSent(OrderConfirmation::class, function ($mail) use ($order) {
            return $mail->order->id === $order->id;
        });
    }

    public function test_order_dispatches_job(): void
    {
        Queue::fake();

        $order = Order::factory()->create();
        ProcessOrder::dispatch($order);

        Queue::assertPushed(ProcessOrder::class);
        Queue::assertPushedOn('orders', ProcessOrder::class);
    }

    public function test_user_notified_on_order(): void
    {
        Notification::fake();

        $user = User::factory()->create();
        $order = Order::factory()->for($user)->create();

        $user->notify(new OrderShipped($order));

        Notification::assertSentTo($user, OrderShipped::class);
    }

    public function test_file_upload(): void
    {
        Storage::fake('s3');

        $file = UploadedFile::fake()->image('photo.jpg');

        $response = $this->postJson('/api/upload', [
            'photo' => $file,
        ]);

        Storage::disk('s3')->assertExists('photos/' . $file->hashName());
    }
}
🌐

Laravel Dusk

Browser Testing

composer require --dev laravel/dusk
php artisan dusk:install

# Run dusk tests
php artisan dusk
<?php
// tests/Browser/LoginTest.php

namespace Tests\Browser;

use App\Models\User;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;

class LoginTest extends DuskTestCase
{
    public function test_user_can_login(): void
    {
        $user = User::factory()->create();

        $this->browse(function (Browser $browser) use ($user) {
            $browser->visit('/login')
                ->type('email', $user->email)
                ->type('password', 'password')
                ->press('Đăng nhập')
                ->assertPathIs('/dashboard')
                ->assertSee('Chào mừng');
        });
    }

    public function test_shopping_cart_flow(): void
    {
        $this->browse(function (Browser $browser) {
            $browser->visit('/products')
                ->click('@add-to-cart-btn')
                ->waitFor('@cart-count')
                ->assertSeeIn('@cart-count', '1')
                ->click('@checkout-btn')
                ->assertPathIs('/checkout');
        });
    }
}
🐛

Pest PHP

Testing với Pest (Laravel 11 default)

<?php
// tests/Feature/PostTest.php

use App\Models\Post;
use App\Models\User;

test('can list posts', function () {
    Post::factory()->count(5)->create();

    $response = $this->getJson('/api/posts');

    $response->assertOk()
        ->assertJsonCount(5, 'data');
});

test('authenticated user can create post', function () {
    $user = User::factory()->create();

    $this->actingAs($user)
        ->postJson('/api/posts', [
            'title' => 'Test Post',
            'content' => 'Content here',
        ])
        ->assertCreated();

    expect(Post::count())->toBe(1);
    expect(Post::first()->title)->toBe('Test Post');
});

test('guest cannot create post', function () {
    $this->postJson('/api/posts', [
        'title' => 'Test',
        'content' => 'Content',
    ])->assertUnauthorized();
});

// Dataset testing
dataset('invalid_emails', [
    'not-an-email',
    'missing@domain',
    '@nodomain.com',
]);

test('rejects invalid emails', function (string $email) {
    $this->postJson('/api/register', ['email' => $email])
        ->assertJsonValidationErrors(['email']);
})->with('invalid_emails');
💡 Pest: Cleaner syntax với test()expect(). Laravel 11 mặc định dùng Pest.
← Bài 11: Unit Testing Bài 13: Performance →