مقاله

تست ایزوله Action و Repository با mock، fake DB و RefreshDatabase — مرز unit و feature.

تست Repository و Action در Laravel — unit test بدون HTTP

تست Repository و Action در Laravel — unit test بدون HTTP

Feature test از HTTP شروع می‌کند — کامل اما کند. وقتی logic در Action و Repository است، unit test مستقیم روی handle() سریع‌تر و دقیق‌تر failure می‌دهد.

تست Action با mock Repository

use Mockery;

it('creates post via action', function () {
    $dto = new CreatePostData('Title', 'title', 'Content');
    $user = User::factory()->make(['id' => 1]);
    $expected = Post::factory()->make(['title' => 'Title']);

    $repo = Mockery::mock(PostRepositoryInterface::class);
    $repo->shouldReceive('create')
        ->once()
        ->with(Mockery::on(fn ($d) => $d['title'] === 'Title'))
        ->andReturn($expected);

    $action = new CreatePostAction($repo);
    $result = $action->handle($dto, $user);

    expect($result->title)->toBe('Title');
});

بدون DB — pure unit. Unit test mock.

تست Action با database واقعی

uses(RefreshDatabase::class);

it('persists post', function () {
    $user = User::factory()->create();
    $dto = new CreatePostData('Test', 'test', 'Body');
    $action = app(CreatePostAction::class);

    $post = $action->handle($dto, $user);

    expect($post)->toBeInstanceOf(Post::class);
    $this->assertDatabaseHas('posts', ['slug' => 'test', 'user_id' => $user->id]);
});

integration سبک — Repository واقعی، HTTP نه.

تست Repository

uses(RefreshDatabase::class);

it('finds published posts', function () {
    Post::factory()->published()->count(2)->create();
    Post::factory()->draft()->create();

    $repo = new EloquentPostRepository();
    $posts = $repo->published();

    expect($posts)->toHaveCount(2);
});

Repository عمدتاً integration test — query درست؟

assert Event

Event::fake([PostCreated::class]);

$action->handle($dto, $user);

Event::assertDispatched(PostCreated::class);

Event/Listener.

assert Queue

Queue::fake();
// action dispatches job
Queue::assertPushed(SendNewsletterJob::class);

DTO در تست

از DTO در arrange — type-safe setup.

Exception testing

it('throws when slug duplicate', function () {
    expect(fn () => $action->handle($duplicateDto, $user))
        ->toThrow(DuplicateSlugException::class);
});

مرز Unit vs Feature

Unit (Action mock)Integration (DB)Feature (HTTP)
سریع، logicquery، transactionroute، middleware، JSON
mock dependencyRepository واقعیکل stack

اشتباهات

  • mock همه چیز حتی Eloquent — تست بی‌معنی
  • Feature test برای هر branch کوچک
  • assertion روی implementation نه outcome

جمع‌بندی

Action unit + Repository integration + Feature critical path = pyramid سالم. Mockery: عمیق. Doubles: Fakes.

test strategy پروژه