Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Middle: питання на співбесіді з теми «Тестування»

Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.

5 питань

Laravel має тестування з коробки поверх PHPUnit; популярна надбудова - Pest із лаконічним синтаксисом.

it('creates a post', function () {
    $user = User::factory()->create();

    $response = $this->actingAs($user)->post('/posts', [
        'title' => 'Hello',
    ]);

    $response->assertRedirect();
    $this->assertDatabaseHas('posts', ['title' => 'Hello']);
});
  • Feature-тести перевіряють HTTP-флоу (більшість тестів); Unit - окремі класи ізольовано.
  • Трейт RefreshDatabase ізолює тести, відкочуючи зміни після кожного; дані генерують фабрики.
  • actingAs($user) автентифікує користувача для захищених маршрутів.

Корисні асерції: assertStatus, assertSee, assertJson, assertDatabaseHas, assertRedirect, assertAuthenticated.

Мокінг - ізоляція від зовнішніх ефектів (API, листи, черги):

Mail::fake();         Mail::assertSent(OrderShipped::class);
Queue::fake();        Queue::assertPushed(ProcessPodcast::class);
Notification::fake(); Http::fake();

// мок сервісу через контейнер
$this->mock(PaymentGateway::class)
    ->shouldReceive('charge')->once()->andReturn(true);

Запуск: php artisan test, --filter=PostTest, --parallel (швидше).

Докладніше в документації: Тестування

Підмінити відповідний фасад фейком: він перехопить відправку, а тест перевірить, що вона сталася з правильними даними.

it('notifies the customer when the order ships', function () {
    Notification::fake();
    Mail::fake();
    Queue::fake();

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

    Notification::assertSentTo($order->customer, OrderShipped::class);
    Mail::assertSent(InvoiceMail::class, fn ($mail) => $mail->hasTo($order->customer->email));
    Queue::assertPushed(SyncWithWarehouse::class);
});

Які фейки є: Mail, Notification, Queue, Bus (бачить ще й ланцюжки й пакети), Event, Storage, Http, Process, Exceptions.

Пастки:

  • Event::fake() без аргументів глушить усі події, включно з подіями моделей, на яких можуть триматися генерація UUID чи Observer. Підробляйте лише потрібні: Event::fake([OrderShipped::class]).
  • Фейк не перевіряє, що робота виконується. Queue::assertPushed() доводить, що завдання поставлено, але не що його handle() працює. Код самого завдання тестують окремо - викликаючи його напряму.
  • Mailable можна перевірити без відправки: (new InvoiceMail($order))->assertSeeInHtml($order->number).

Докладніше в документації: Тестування надсилання листів

HTTP-тести Laravel мають методи для JSON-запитів і перевірки відповіді.

it('creates a post via the API', function () {
    Sanctum::actingAs(User::factory()->create(), ['posts:write']);

    $this->postJson('/api/posts', ['title' => 'Черги в Laravel'])
        ->assertCreated()
        ->assertJsonPath('data.title', 'Черги в Laravel')
        ->assertJsonStructure(['data' => ['id', 'title', 'created_at']]);
});

it('rejects a post without a title', function () {
    Sanctum::actingAs(User::factory()->create(), ['posts:write']);

    $this->postJson('/api/posts', [])
        ->assertUnprocessable()
        ->assertInvalid(['title']);
});

Що варто знати:

  • postJson() ставить Accept: application/json, тож помилки валідації приходять як 422 з JSON, а не редиректом.
  • assertJsonPath() перевіряє одне значення за шляхом, assertJson() - частковий збіг, assertExactJson() - точний.
  • Флюентні перевірки дозволяють описати структуру з типами й гарантувати, що зайвих полів немає:
->assertJson(fn (AssertableJson $json) => $json
    ->where('data.id', $post->id)
    ->whereType('data.title', 'string')
    ->missing('data.author.password')
    ->etc());
  • Автентифікація: actingAs() для сесії, Sanctum::actingAs() з переліком здатностей для токенів.

Докладніше в документації: Тестування JSON API

Керувати часом застосунку з тесту, а не чекати.

it('expires a subscription after 30 days', function () {
    $this->freezeTime();

    $subscription = Subscription::factory()->create();

    $this->travel(31)->days();

    expect($subscription->fresh()->isExpired())->toBeTrue();
});

Інструменти:

  • freezeTime() - зупиняє now(), щоб час не зсувався між рядками тесту.
  • travel(5)->minutes(), travelTo($date), travelBack() - зсувають час застосунку.
  • Те саме вміє Carbon::setTestNow().

Що варто винести з цього: такий тест працює, лише поки код бере час через now() чи Carbon. Виклики time(), date() чи new \DateTime() бачать справжній годинник і тест обійдуть.

Чистіший шлях для доменного коду - передавати момент явно:

public function isExpired(CarbonImmutable $now): bool
{
    return $this->ends_at->lessThan($now);
}

Тоді тест взагалі не потребує підміни часу, а метод стає чистою функцією. Корисні випадки для перевірки: межі (рівно 30 днів), перехід доби, часові пояси й літній час.

Докладніше в документації: Робота з часом

Підмінити HTTP-клієнт Laravel фейковими відповідями, щоб тест не залежав від мережі й чужого сервісу.

it('imports exchange rates', function () {
    Http::fake([
        'api.rates.example/*' => Http::response(['usd' => 41.2], 200),
        '*' => Http::response(status: 500),
    ]);

    (new ImportRates)->handle();

    expect(Rate::latest()->value('usd'))->toBe(41.2);

    Http::assertSent(fn (Request $request) => str_contains($request->url(), 'api.rates.example')
        && $request->hasHeader('Authorization'));
});

Що варто знати:

  • Шаблони URL з * підмінюють групи запитів. Запит, що не збігся з жодним шаблоном, піде в мережу насправді.
  • Http::preventStrayRequests() забороняє такі запити: будь-який непідмінений кидатиме виняток. Його вмикають у TestCase чи Pest.php для всього набору.
  • Http::sequence() повертає різні відповіді на послідовні виклики - так перевіряють повтори після помилки.
  • Помилки теж тестують: таймаут, 429, 500 - саме там зазвичай ховаються баги обробки.

Межа фейку: він закріплює ваше уявлення про чужий API. Коли провайдер змінить формат, тести лишаться зеленими - тому корисні контрактні перевірки чи хоча б моніторинг справжніх відповідей у проді.

Докладніше в документації: HTTP-клієнт: тестування