Ось готовий урок, створений спеціально для тебе в стилі Девіда Малана (CS50). Вмикаймо уяву, ми в аудиторії Сандерс, і поїхали! 🚀
🧪 Тестування React-компонентів: Ваша подушка безпеки
Вітаю, друзі! Це CS50... тобто, це ваш урок з React!
1. 🔥 Вступ: Чому ми взагалі про це говоримо?
Уявіть ситуацію. П'ятниця, вечір. Ви тільки-но закінчили роботу над крутою фічею для вашого інтернет-магазину — кнопкою "Купити в один клік". Ви перевірили її — працює! Ви "пушите" код на сервер, закриваєте ноутбук і йдете пити пиво з друзями.
А в суботу вранці вас будить дзвінок розлюченого менеджера: "Сайт лежить! Ніхто не може нічого купити! Що ти наробив?!"
Виявляється, ваша "маленька зміна" випадково зламала логін користувача. Але ви ж це не перевіряли, правда? Ви перевіряли лише кнопку.
Питання до вас: Чи хотіли б ви мати робота, який за 3 секунди переклацає весь ваш сайт, кожну кнопочку, кожне поле вводу, і скаже: "Все ок, друже, нічого не зламалося"?
Звісно, хотіли б.
Без тестування розробка схожа на будівництво хмарочоса без будівельного рівня. Може, воно і стоїть рівно... поки що. Але чи ризикнете ви туди зайти?
Сьогодні ми навчимося будувати цю "подушку безпеки" для ваших React-компонентів.
2. 🧠 Теоретична база: Що там "під капотом"?
Ми не будемо заглиблюватися в нудну термінологію, але є два інструменти, імена яких ви маєте знати. Це як Бетмен і Робін у світі тестів:
- Jest — це наш "запускач". Він знаходить файли з тестами, виконує їх і каже: ✅ Passed або ❌ Failed.
- React Testing Library (RTL) — це інструмент, який допомагає нам "рендерити" компоненти та взаємодіяти з ними так, як це робить жива людина.
Як це працює? (Інтуїтивне пояснення)
Коли ви пишете код React, він живе у браузері (Chrome, Firefox). Там є DOM-дерево. Але тести запускаються в консолі (терміналі). Там немає браузера!
Як же ми тестуємо?
Ми використовуємо JSDOM — це симуляція браузера всередині Node.js.
RTL бере ваш компонент <Button />, створює його віртуальну HTML-версію в пам'яті комп'ютера і дозволяє нам питати: "Гей, а там є текст 'Натисни мене'?" або "А що буде, якщо я клікну сюди?".
Золоте правило RTL: Тестуйте так, як користується програмою юзер, а не так, як вона написана всередині. Юзер не знає про ваші
state,propsчиuseEffect. Він бачить кнопки і текст. Отже, і тести мають шукати кнопки і текст.
3. 🧪 Приклади: Від "Hello World" до кліків
Приклад 1: Простий рендер
У нас є компонент Greeting, який просто вітає користувача.
// Greeting.js
const Greeting = ({ name }) => {
return <h1>Привіт, {name}!</h1>;
};
export default Greeting;
Як ми це протестуємо? Нам треба: 1. "Намалювати" компонент. 2. Знайти текст на "екрані". 3. Переконатися, що текст правильний.
// Greeting.test.js
import { render, screen } from '@testing-library/react';
import Greeting from './Greeting';
test('має показувати правильне привітання', () => {
// 1. Рендеримо
render(<Greeting name="Андрій" />);
// 2. Шукаємо елемент (якщо не знайде — тест впаде)
const element = screen.getByText(/Привіт, Андрій!/i);
// 3. Стверджуємо (Assertion)
expect(element).toBeInTheDocument();
});
Бачите, як просто? Це майже англійська мова: "Expect element to be in the document".
Приклад 2: Інтерактивність (Кнопка)
Ускладнимо задачу. Компонент з лічильником.
// Counter.js
import { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
return (
<div>
<p>Рахунок: {count}</p>
<button onClick={() => setCount(count + 1)}>Збільшити</button>
</div>
);
};
export default Counter;
Питання до студента: Що ми маємо зробити в тесті, щоб перевірити, чи працює кнопка? (Пауза для роздумів...) Правильно! Нам треба: знайти кнопку, "клікнути" на неї програмно і перевірити, чи змінився текст.
// Counter.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';
test('збільшує лічильник при кліку', () => {
render(<Counter />);
// Перевіряємо початковий стан
const counterText = screen.getByText(/Рахунок: 0/i);
expect(counterText).toBeInTheDocument();
// Знаходимо кнопку
const button = screen.getByText(/Збільшити/i);
// Імітуємо клік! (Fire Event - "Вистрілити подією")
fireEvent.click(button);
// Перевіряємо результат
expect(screen.getByText(/Рахунок: 1/i)).toBeInTheDocument();
});
Чому результат такий? Тому що fireEvent.click(button) змушує React запустити функцію onClick, змінити стейт і перерендерити віртуальний DOM.
4. 🛠 Практична частина
Час забруднити руки кодом! Ось ваші завдання.
Завдання 1: "Копіпаст-майстер"
Створіть компонент StatusMessage, який приймає проп isSuccess. Якщо true — показує "Успіх!", якщо false — "Помилка!". Напишіть два тести для обох сценаріїв.
Завдання 2: "Зникнення"
У вас є кнопка, яка при натисканні зникає (не просто ховається CSS-ом, а взагалі видаляється з DOM).
Підказка: Використовуйте queryByText замість getByText, щоб перевірити відсутність елемента (воно поверне null, а не помилку).
expect(screen.queryByText('Кнопка')).not.toBeInTheDocument();
Завдання 3: "Шерлок Холмс"
У вас є інпут: <input placeholder="Введіть email" />.
Напишіть тест, який знаходить цей інпут саме за плейсхолдером (getByPlaceholderText), вводить туди текст і перевіряє, чи змінилося значення (value).
Завдання 4: Міні-кейс
Створіть компонент Spoiler. Він показує кнопку "Показати секрет". Коли клікаєш — з'являється текст "React — це просто JS".
Напишіть тест:
1. Тексту спочатку немає.
2. Клікаємо кнопку.
3. Текст з'явився.
Завдання 5: А що, якщо?
Що буде, якщо в тесті з Прикладу 2 ми зробимо fireEvent.click(button) два рази підряд? Який текст ми маємо очікувати? Перевірте це на практиці.
5. 💡 Мислення як у розробника
Послухайте уважно. Новачки часто роблять одну й ту ж помилку. Вони намагаються тестувати реалізацію, а не поведінку.
❌ Поганий тест (як думає новачок):
"Я хочу перевірити, чи змінився state компонента на true."
(Це погано, бо якщо ви перейменуєте змінну стейту, тест впаде, хоча програма працює).
✅ Хороший тест (як думає сеньйор):
"Я хочу перевірити, чи побачив користувач повідомлення про помилку після кліку."
(Нам байдуже, як це зроблено всередині — через useState, Redux чи магію вуду. Головне — результат для юзера).
Порада з практики: Якщо ваш тест занадто складний і займає 50 рядків коду для простої кнопки — швидше за все, ви робите щось не так. Тест має бути простою історією: Підготував -> Зробив дію -> Перевірив результат.
6. 🧩 Підсумок
Отже, що ми сьогодні зробили?
- Зрозуміли, що тести — це не бюрократія, а наш спокійний сон.
- Навчилися рендерити компоненти у "віртуальному браузері".
- Навчилися знаходити елементи та клікати по них програмно.
- Зрозуміли філософію: Тестуй як юзер, а не як програміст.
Тепер ви вмієте писати базові юніт-тести. Ви вже кращі за 50% новачків, які "тестують" свій код, просто оновлюючи сторінку в браузері 100 разів на день.
Тизер наступного уроку: Але що робити, коли наш компонент хоче завантажити дані з сервера (API)? Ми ж не хочемо робити реальні запити в тестах? На наступному уроці ми поговоримо про Mocking (Моки) — мистецтво створення "фейкових" серверів.
А поки що — пишіть код, ламайте його, пишіть тести і лагодьте! Побачимось! 👋