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

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

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

3 питання

Знімкові тести (snapshot) зберігають результат при першому запуску у файл, а при наступних - порівнюють з ним.

it('серіалізує замовлення для API', () => {
  expect(toApiPayload(order)).toMatchSnapshot();
});

it('форматує адресу', () => {
  expect(formatAddress(address)).toMatchInlineSnapshot(`"м. Київ, вул. Хрещатик, 1"`);
});

Оновлення знімків після навмисної зміни: npx vitest -u.

Коли знімки корисні:

  • великі структури даних, які складно перевірити вручну: payload для API, згенерована конфігурація, результат парсера;
  • виявлення неочікуваних змін у серіалізації: додали поле - знімок це покаже;
  • inline-знімки для коротких значень - видно прямо в тесті, рев'ю бачить очікування.

Коли шкодять:

  • знімки всієї розмітки компонента змінюються від кожної правки верстки чи класу. Розробник звикає запускати -u не дивлячись - і знімок перестає щось перевіряти;
  • великі знімки ніхто не читає на рев'ю: diff на 300 рядків приймається «на віру»;
  • нестабільні дані (дати, випадкові id) змушують фіксувати чи маскувати значення, інакше тест падає щоразу;
  • знімок замість думки: тест не каже, що важливо в результаті. Явна перевірка expect(payload.total).toBe(1999) документує намір, знімок - ні.

Візуальна регресія порівнює знімки екрана:

await expect(page).toHaveScreenshot('checkout.png', {
  maxDiffPixelRatio: 0.01,
  mask: [page.getByTestId('current-date')],
});

Ловить те, що інші тести не бачать: зламаний CSS, зсунуту верстку, зниклі іконки, проблеми темної теми.

Складнощі візуальних тестів:

  • рендеринг шрифтів і згладжування відрізняються між ОС і навіть версіями браузера - знімки, зроблені на Mac, не збігаються з Linux у CI. Еталонні знімки генерують у тому самому середовищі (Docker-образ Playwright), де запускаються тести;
  • динамічний вміст (дати, реклама, аватари, анімації) - маскувати й вимикати;
  • поріг розбіжності - надто суворий дає хибні падіння, надто м'який пропускає реальні зміни;
  • зберігання еталонів у репозиторії збільшує його розмір.

Практичний підхід: небагато візуальних тестів для ключових сторінок і компонентів дизайн-системи, явні перевірки поведінки - для логіки, знімки даних - для великих структур з обов'язковим переглядом diff на рев'ю.

Докладніше в документації: Playwright: візуальні порівняння

У Laravel-проєкті фронтенд (Blade з Alpine, Livewire, Inertia з Vue чи React) тісно пов'язаний з бекендом. Браузерний тест має перевіряти обидві частини разом - і питання в тому, з якого боку його писати.

Pest browser tests (плагін pestphp/pest-plugin-browser, працює на Playwright):

it('користувач підписується на розсилку', function () {
    $page = visit('/');

    $page->fill('email', 'olena@example.com')
        ->click('Підписатися')
        ->assertSee('Дякуємо за підписку')
        ->assertNoJavaScriptErrors();

    expect(Subscriber::where('email', 'olena@example.com')->exists())->toBeTrue();
});

it('сторінки відкриваються без помилок', function () {
    visit(['/', '/blog', '/jobs'])->assertNoSmoke();
});
  • той самий процес і база, що в PHP-тестах: фабрики, RefreshDatabase, actingAs(), Mail::fake() працюють як у звичайних feature-тестах;
  • перевірка бази поруч з перевіркою інтерфейсу - без API для підготовки даних;
  • smoke-тести (assertNoSmoke, assertNoJavaScriptErrors) дешево ловлять зламані сторінки й помилки в консолі;
  • inDarkMode(), мобільні пристрої, знімки екрана для візуальних порівнянь.

Playwright напряму (тести на JavaScript/TypeScript):

  • окремий застосунок запущений як справжній сервер (webServer у конфігурації), тести бачать його лише через HTTP;
  • підготовка даних - через сидери, спеціальні тестові ендпойнти чи API, що ускладнює ізоляцію;
  • повний API Playwright: перехоплення мережі, кілька вкладок і контекстів, трасування, компонентні тести;
  • природно для команди, що пише фронтенд на TypeScript, і для SPA, де бекенд - окремий сервіс.

Як обрати:

Ситуація Що зручніше
Blade, Livewire, Inertia в одному репозиторії з Laravel Pest browser tests
команда пише переважно на PHP Pest
окремий SPA-фронтенд, бекенд лише API Playwright
складна клієнтська логіка: офлайн, кілька вкладок, мережеві умови Playwright

У CI:

  • браузери треба встановити (npx playwright install --with-deps chromium) і закешувати;
  • ассети зібрати (npm run build), інакше сторінки без JavaScript і CSS;
  • паралельний запуск і шардування для довгих наборів;
  • знімки екрана й траси як артефакти збирання - щоб розбиратися з падіннями без локального відтворення.

Розподіл рівнів: браузерні тести - для критичних сценаріїв і smoke-перевірок сторінок, Livewire- і feature-тести - для логіки компонентів і контролерів, Vitest - для складної клієнтської логіки окремо від браузера.

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

На старті будь-які тести швидкі. Через рік набір з тисячі тестів може йти 15 хвилин, падати випадково й гальмувати кожен pull request. Структура з самого початку визначає, чи станеться це.

1. Правильний рівень для кожної перевірки:

  • логіка (розрахунки, перетворення, валідація) - винесена в чисті функції й перевіряється unit-тестами в середовищі node, без DOM;
  • поведінка компонентів - інтеграційні тести з Testing Library;
  • критичні сценарії - кілька E2E.

Найпоширеніша причина повільного набору - перевірка логіки через E2E: кожен варіант знижки перевіряється відкриттям браузера.

2. Середовище за потребою: jsdom у всіх файлах сповільнює й ті тести, яким DOM не потрібен. Середовище задають для конкретних файлів чи проєктів:

// vitest.config.js
export default defineConfig({
  test: {
    projects: [
      { test: { name: 'unit', environment: 'node', include: ['**/*.unit.test.js'] } },
      { test: { name: 'dom', environment: 'happy-dom', include: ['**/*.dom.test.js'] } },
    ],
  },
});

3. Ізоляція й детермінованість:

  • кожен тест готує свої дані й не залежить від порядку запуску;
  • моки й глобальні заглушки відновлюються після кожного тесту (restoreMocks, unstubGlobals у конфігурації);
  • час, випадкові значення й мережа - під контролем тесту (фейкові таймери, vi.setSystemTime, MSW).

4. Швидкий зворотний зв'язок:

  • vitest --changed - лише тести, пов'язані зі зміненими файлами;
  • у CI - паралельний запуск і шардування: vitest run --shard=1/4;
  • E2E - окремою задачею CI, що не блокує швидкі перевірки.

5. Боротьба з нестабільними тестами: тест, що падає випадково, швидко вчить команду ігнорувати червоний CI. Нестабільний тест або виправляють одразу, або тимчасово виключають (з задачею на виправлення), але не перезапускають мовчки.

6. Тести як код:

  • спільні фабрики тестових даних замість копіювання об'єктів у кожному тесті;
  • допоміжні функції для монтування з провайдерами;
  • назви тестів описують поведінку: «показує помилку для порожньої пошти», а не «test 3».

7. Що прибирати: тести, що перевіряють реалізацію (внутрішні виклики, точну розмітку), знімки, які оновлюють не дивлячись, і дублікати тієї самої перевірки на різних рівнях.

Метрики здоров'я набору: час прогону в CI, частка нестабільних тестів, кількість тестів, змінених при рефакторингу без зміни поведінки. Остання - найкращий індикатор того, що тести перевіряють деталі реалізації.

Докладніше в документації: Vitest: середовище тестів