Testing ו-MVP
Testing, MVP ו-Git Workflow
שיעור כפול: חצי ראשון — Testing אוטומטי עם Jest ו-React Testing Library. חצי שני — סדנת קוד: מהלומד למפתח — MVP, Debugging מקצועי ו-README מנצח.
- Testing Pyramid — Unit, Integration, E2E: כמה מכל סוג?
- Jest: describe, test, expect — מבנה בדיקה בסיסית
- Matchers: toBe, toEqual, toContain, toThrow
- React Testing Library: render, screen, queries
- userEvent > fireEvent — סימולציית אינטראקציה אמיתית
- Async Tests: findBy, waitFor — בדיקת קוד אסינכרוני
- Mocking: jest.fn(), jest.mock() — בידוד תלויות
- MVP — בניית הפיצ'ר המינימלי End-to-End
- Git Workflow — Branches, Commits, Pull Requests
- Debugging מקצועי — DevTools, Logs, Stack Trace
למה Testing? הבעיה שבדיקות פותרות
בפרויקט קטן אפשר לבדוק ידנית. אבל בפרויקט של 50 קומפוננטות — כל שינוי קטן יכול לשבור משהו שלא ידעת שקיים.
Regression Bug = באג שנשבר בגלל שינוי שלכאורה לא קשור. בדיקות אוטומטיות תופסות אותם לפני שהמשתמש רואה אותם.
| בלי בדיקות | עם בדיקות |
|---|---|
| שינוי קוד → פחד לפרוס | שינוי קוד → CI/CD מוודא שהכל עובד |
| Regression Bugs גלויים רק ב-Production | Regression Bugs נתפסים מיד ב-Push |
| Refactoring מסוכן | Refactoring בטוח — הבדיקות יאמרו אם שברת |
| תיעוד מיושן לא מעודכן | הבדיקות הן תיעוד חי של ההתנהגות |
Integration Tests (אמצע): בינוני — בודקות שמספר יחידות עובדות יחד.
E2E Tests (פסגה): מעט — איטיות, יקרות, מדמות משתמש אמיתי (Cypress, Playwright).
לפרויקט בגאמא: Unit + Component Tests מספיקים להרשים.
Jest — יסודות
Jest הוא Test Runner — מוצא קבצי בדיקה, מריץ אותם, ומדווח על תוצאות. CRA ו-Vite כוללים אותו מהקופסה.
מבנה בדיקה בסיסית
// utils/math.test.js (או math.spec.js — שניהם תקינים)
// describe: מקבץ בדיקות קשורות
describe('פונקציות מתמטיות', () => {
// test / it — בדיקה אחת
test('חיבור שני מספרים', () => {
const result = add(2, 3);
expect(result).toBe(5); // Assertion — מה אנחנו מצפים
});
it('מחזיר שגיאה על קלט לא-מספרי', () => {
expect(() => add('a', 3)).toThrow('קלט לא תקין');
});
});
// הרצה: npx jest (או npm test ב-CRA)Matchers — איך בודקים ערכים
| Matcher | שימוש | דוגמה |
|---|---|---|
| toBe | השוואה מדויקת (===) | expect(2+2).toBe(4) |
| toEqual | השוואת אובייקטים/מערכים | expect(obj).toEqual({a:1}) |
| toContain | מכיל ערך (מערך/string) | expect([1,2]).toContain(2) |
| toHaveLength | אורך מסוים | expect([1,2,3]).toHaveLength(3) |
| toThrow | זורק שגיאה | expect(fn).toThrow('msg') |
| not.toBe | שלילת כל Matcher | expect(1).not.toBe(2) |
Unit Test אמיתי — Arrange-Act-Assert
// utils/todos.js
export function filterTodos(todos, filter) {
if (filter === 'active') return todos.filter(t => !t.done);
if (filter === 'done') return todos.filter(t => t.done);
return todos;
}
// utils/todos.test.js
import { filterTodos } from './todos';
const mockTodos = [
{ id: 1, text: 'ללמוד React', done: false },
{ id: 2, text: 'לכתוב בדיקות', done: true },
{ id: 3, text: 'לפרוס פרויקט', done: false },
];
describe('filterTodos', () => {
test('filter=all מחזיר את כולם', () => {
// Arrange: נתונים מוכנים (mockTodos)
// Act: הפעל
const result = filterTodos(mockTodos, 'all');
// Assert: בדוק
expect(result).toHaveLength(3);
});
test('filter=active מחזיר רק לא-גמורים', () => {
const result = filterTodos(mockTodos, 'active');
expect(result).toHaveLength(2);
expect(result.every(t => !t.done)).toBe(true);
});
test('filter=done מחזיר רק גמורים', () => {
const result = filterTodos(mockTodos, 'done');
expect(result).toHaveLength(1);
expect(result[0].id).toBe(2);
});
});- Arrange: הכן את הנתונים (mockTodos)
- Act: הפעל את הפונקציה (filterTodos(mockTodos, 'active'))
- Assert: בדוק את התוצאה (expect(result).toHaveLength(2))
- בסדנה — בדיקה שכתובה לפי AAA נראית מקצועית ומובנת
expect([1,2,3]).toHaveLength(2)
React Testing Library — בדיקת קומפוננטות
RTL בנויה על עיקרון אחד: בדוק מה המשתמש רואה ועושה — לא את המימוש הפנימי. אל תבדוק state פנימי, אל תבדוק שמות פונקציות.
render ו-screen
import { render, screen } from '@testing-library/react';
import Button from './Button';
test('מציג את ה-label של הכפתור', () => {
// render: מרנדר קומפוננטה לתוך DOM וירטואלי
render(<Button label='שמור' => {}} />);
// screen: API לגישה ל-DOM שרונדר
const button = screen.getByText('שמור');
expect(button).toBeInTheDocument();
});
test('כפתור disabled לא ניתן ללחיצה', () => {
render(<Button label='שמור' => {}} disabled />);
expect(screen.getByText('שמור')).toBeDisabled();
});Queries — איך מוצאים elements
| Query | מחזיר | זורק אם לא נמצא? | מתי משתמשים |
|---|---|---|---|
| getBy... | element | כן — מיד | כשElement חייב להיות שם |
| queryBy... | element/null | לא | כשרוצים לבדוק שElement לא קיים |
| findBy... | Promise | כן — async | כשElement מופיע אחרי המתנה |
| getAllBy... | element[] | כן אם 0 | כשצפויים כמה elements |
ByRole בודק גם נגישות (Accessibility) — button, link, heading, checkbox, textbox.
סדר עדיפויות: ByRole > ByLabelText > ByPlaceholder > ByText > ByTestId
userEvent — סימולציית אינטראקציה
import userEvent from '@testing-library/user-event';
test('הוספת Todo חדש', async () => {
render(<TodoApp />);
const input = screen.getByPlaceholderText('הוסף משימה...');
const button = screen.getByRole('button', { name: 'הוסף' });
// הקלד טקסט — userEvent מדמה אינטראקציה אמיתית
await userEvent.type(input, 'ללמוד Testing');
await userEvent.click(button);
// בדוק שהטקסט מופיע ברשימה
expect(screen.getByText('ללמוד Testing')).toBeInTheDocument();
// בדוק שה-input התרוקן
expect(input).toHaveValue('');
});Async Tests ו-Mocking
קומפוננטות שעושות fetch לא מציגות נתונים מיד. RTL מספקת כלים לבדיקת מצבים אסינכרוניים, ו-Mocking מאפשר בידוד מ-API אמיתי.
findBy ו-waitFor
import { render, screen, waitFor } from '@testing-library/react';
test('מציג שם משתמש אחרי טעינה', async () => {
render(<UserProfile userId={1} />);
// בתחילה — Spinner מוצג
expect(screen.getByText('טוען...')).toBeInTheDocument();
// findByText: מחכה עד שElement מופיע (timeout: 1000ms)
const name = await screen.findByText('Alice');
expect(name).toBeInTheDocument();
// Spinner נעלם
expect(screen.queryByText('טוען...')).not.toBeInTheDocument();
});
// waitFor: מחכה עד שתנאי מתקיים
test('מציג Error אחרי כשל', async () => {
render(<UserProfile userId={999} />);
await waitFor(() => {
expect(screen.getByText(/שגיאה/)).toBeInTheDocument();
});
});Mock של fetch — בידוד מ-API אמיתי
// לפני הבדיקות — Mock גלובלי לfetch
global.fetch = jest.fn();
beforeEach(() => {
// הגדר מה fetch יחזיר בכל בדיקה
fetch.mockResolvedValueOnce({
ok: true,
json: async () => ({ id: 1, name: 'Alice', email: 'alice@test.com' }),
});
});
afterEach(() => {
jest.clearAllMocks(); // נקה Mocks אחרי כל בדיקה
});
test('מציג פרטי משתמש מה-API', async () => {
render(<UserProfile userId={1} />);
expect(await screen.findByText('Alice')).toBeInTheDocument();
expect(screen.getByText('alice@test.com')).toBeInTheDocument();
// בדוק שfetch נקרא עם ה-URL הנכון
expect(fetch).toHaveBeenCalledWith('/api/users/1');
expect(fetch).toHaveBeenCalledTimes(1);
});jest.fn() — Mock לפונקציה
test('קורא ל-onDelete כשלוחצים מחק', async () => {
const mockOnDelete = jest.fn(); // פונקציה מזויפת שמתעדת קריאות
render(<TodoItem
todo={{ id: 1, text: 'משימה', done: false }}
/>);
await userEvent.click(screen.getByRole('button', { name: 'מחק' }));
expect(mockOnDelete).toHaveBeenCalledTimes(1);
expect(mockOnDelete).toHaveBeenCalledWith(1); // עם ה-id הנכון
});! טעות נפוצה — בדיקות שמשפיעות אחת על השנייה ›
כשבדיקה שינתה State גלובלי (localStorage, Mock) ולא ניקתה — הבדיקה הבאה מקבלת מצב לא נקי.
תסמין: הבדיקה עוברת כשמריצים אותה לבד אבל נכשלת עם כל הבדיקות.
beforeEach(() => {
localStorage.clear();
jest.clearAllMocks(); // מאפס call history של כל המocks
});MVP — מהלומד למפתח
הגיע הזמן לחבר הכל למוצר אחד עובד End-to-End. נהפוך מ"לומדים" ל"מפתחים" — MVP, Debugging מקצועי, ו-README שמרשים בוחנים.
| כיצד בא לידי ביטוי | שלב במיון | נושא |
|---|---|---|
| הסבר מעבר המידע מUI לשרת למסד ובחזרה | סדנה וראיונות | פיצ'ר End-to-End |
| איתור תקלות בזמן אמת, שימוש ב-Logs | סדנה מעשית | Debugging |
| Branching מסודר + README מקצועי ב-GitHub | ראיון מקצועי | Git Workflow |
MVP — בניית הפיצ'ר המינימלי
הטעות הנפוצה: לנסות לבנות הכל בו-זמנית. זה מתכון לכישלון.
גישת MVP: בנה את הגרסה הרזה ביותר שמספקת את הערך המרכזי. תכונות "Nice to have" — לאחר שה-Core עובד.
שבירה נכונה: ← טופס Login (30 דק') ← Endpoint POST /login (30 דק') ← JWT Token (30 דק') ← Protected Route (30 דק')
Git Workflow — כמו מהנדס
# כל פיצ'ר חדש = Branch נפרד
git checkout -b feature/user-auth
# Commits קטנים עם הודעות משמעותיות
git commit -m "feat: add login form validation"
git commit -m "fix: prevent empty password submission"
git commit -m "feat: connect login to /api/auth endpoint"
# לאחר שהפיצ'ר עובד — מיזוג ל-main
git checkout main
git merge feature/user-authDebugging מקצועי — מתודולוגיה מדעית
// באג נפוץ — שכחת await מול MongoDB
// ❌ קוד פגום — שולח Promise, לא נתונים
app.get('/api/users', (req, res) => {
const allUsers = User.find({}); // חסר await!
res.json(allUsers); // שולח [object Promise]
});
// ✅ קוד תקין
app.get('/api/users', async (req, res) => {
try {
const allUsers = await User.find({});
res.json(allUsers);
} catch (err) {
res.status(500).json({ error: err.message });
}
});README מנצח
# 🛡️ Todo Security App
מערכת ניהול משימות מאובטחת עם אימות JWT
## Tech Stack
- **Frontend:** React 18, TypeScript, Redux Toolkit
- **Backend:** Node.js, Express, JWT
- **Database:** MongoDB (Mongoose)
## התקנה
```bash
# Clone
git clone https://github.com/username/todo-app
# התקן תלויות
npm install
cd client && npm install
# הגדר משתני סביבה
cp .env.example .env
# הרץ
npm run dev
```
## API Endpoints
| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | /api/auth/login | התחברות |
| GET | /api/todos | שליפת משימות |
| POST | /api/todos | יצירת משימה |Testing עם Redux ו-Context
קומפוננטות שמשתמשות ב-Redux Store צריכות Provider בזמן הבדיקה. Custom Render Wrapper פותר זאת בצורה אלגנטית.
// test-utils.tsx — Wrapper שמכיל את כל ה-Providers
import { render } from '@testing-library/react';
import { Provider } from 'react-redux';
import { configureStore } from '@reduxjs/toolkit';
import todosReducer from '../store/todosSlice';
// מייצר Store חדש לכל בדיקה — מניעת זיהום בין בדיקות
function renderWithProviders(ui, preloadedState = {}) {
const store = configureStore({
reducer: { todos: todosReducer },
preloadedState,
});
function Wrapper({ children }) {
return <Provider store={store}>{children}</Provider>;
}
return { store, ...render(ui, { wrapper: Wrapper }) };
}
// שימוש בבדיקה:
test('מציג Todos מה-Store', () => {
const preloadedState = {
todos: [{ id: 1, text: 'ללמוד Redux', done: false }],
};
renderWithProviders(<TodoList />, preloadedState);
expect(screen.getByText('ללמוד Redux')).toBeInTheDocument();
});- renderWithProviders מקבל preloadedState — מצב ראשוני ל-Store
- כך כל בדיקה מתחילה עם נתונים ידועים, ללא תלות בבדיקות אחרות
- בסדנה — Custom Render Utility מראה שאתה מבין Testing מקצועי
אתגר מעשי — בדיקות — Mocks ו-Test Runner
שיא השיעור: לבנות רכיב עובד עם זווית אבטחה. ההגשה נבדקת אוטומטית.
סיכום — שיעור 21
עברנו משלב הלמידה לשלב ההנדסה. Testing מקצועי + MVP + Git Workflow + README = פרויקט שמרשים בוחנים גאמא.
- Testing Pyramid: Unit (הרבה) → Integration (בינוני) → E2E (מעט)
- Jest: describe / test / expect — מבנה בדיקה בסיסית
- Matchers: toBe, toEqual, toContain, toHaveLength, toThrow
- Arrange-Act-Assert: הכנה, הפעלה, בדיקה — המבנה הנכון לכל test
- RTL: render + screen — עיקרון: בדוק מה המשתמש רואה
- Queries: getBy (חובה) | queryBy (אופציונלי) | findBy (async)
- ByRole עדיף — בודק נגישות + פונקציונליות בו-זמנית
- userEvent > fireEvent — מדמה אינטראקציה אמיתית, כל הפונקציות async
- Mock: jest.fn() לפונקציה | jest.mock() לספרייה
- beforeEach / afterEach: ניקוי Mocks ו-localStorage לפני/אחרי בדיקה
- Redux Testing: renderWithProviders עם preloadedState לבידוד מלא
- MVP: פיצ'ר ליבה End-to-End לפני תכונות "Nice to have"
- Git Workflow: Branch לכל פיצ'ר + Commits משמעותיים + README מקצועי
- כתוב בדיקות ל-filterTodos, addTodo, toggleTodo מהפרויקט שלך
- כתוב בדיקת Component ל-TodoItem — render, click, delete
- שיעור 22: Build, Deployment ו-CI/CD — מהקוד ל-Production
שאלות תרגול ותשובות (10)
שאלה 1.1 · מה זה Regression Bug?
- א. באג שנשבר בגלל שינוי קוד שלכאורה אינו קשור, כלומר תכונה שעבדה תקין הפסיקה לעבוד, ובדיקות אוטומטיות תופסות אותו עוד לפני העלייה לייצור כאן
- ב. באג שקיים מאז כתיבת הקוד הראשון: הנחה שגויה, אף ש-Regression הוא באג שנשבר בגלל שינוי לא קשור ותכונה שעבדה הפסיקה ולא באג מקורי ותיק כלשהו
- ג. באג שנמצא רק ב-Production ולא בפיתוח: הנחה שגויה, אף ש-Regression הוא באג שנשבר בגלל שינוי לא קשור ותכונה שעבדה הפסיקה ולא נבדל לפי הסביבה
- ד. שגיאת TypeScript שנמצאת בזמן קומפילציה: הנחה שגויה, אף ש-Regression הוא באג שנשבר בגלל שינוי לא קשור ותכונה שעבדה הפסיקה ולא שגיאת טיפוס
✓ תשובה: א) — Regression Bug הוא כשפיצ'ר שעבד תקין פתאום נשבר בגלל שינוי לא קשור. לדוגמה: תיקנת את כפתור הכניסה, ובמקרה שברת את כפתור ההרשמה. בדיקות אוטומטיות תופסות זאת מיד.
שאלה 1.2 · לפי Testing Pyramid, איזה סוג בדיקה כדאי לכתוב הכי הרבה?
// utils/math.test.js (או math.spec.js — שניהם תקינים)
// describe: מקבץ בדיקות קשורות
describe('פונקציות מתמטיות', () => {
// test / it — בדיקה אחת
test('חיבור שני מספרים', () => {
const result = add(2, 3);
expect(result).toBe(5); // Assertion — מה אנחנו מצפים
});
it('מחזיר שגיאה על קלט לא-מספרי', () => {
expect(() => add('a', 3)).toThrow('קלט לא תקין');
});
});
// הרצה: npx jest (או npm test ב-CRA)- א. Unit Tests, מכיוון שהם מהירים וזולים ומבודדים וקלים לתחזוקה, ולכן הם בסיס הפירמידה בעוד E2E יקרות ואיטיות ונכתבות לתהליכים קריטיים בלבד
- ב. E2E Tests שכן הן הכי מדויקות ומדמות משתמש אמיתי: הנחה שגויה, אף שהן איטיות ויקרות והבסיס הוא Unit Tests המהירים והזולים והמבודדים בפירמידה
- ג. Integration Tests שכן הן בודקות את כל המערכת יחד: הנחה שגויה, אף שהבסיס של הפירמידה הוא Unit Tests המהירים והזולים והקלים לתחזוקה בכמות רבה
- ד. שוויון שכן כדאי כמות שווה מכל סוג בדיקה: הנחה שגויה, אף שהבסיס הוא Unit Tests שכותבים בכמות הגדולה ביותר בעוד E2E נכתבות מעט בלבד בפירמידה
✓ תשובה: א) — Unit Tests הם הבסיס של הפירמידה: מהירות לכתיבה, מהירות להרצה, קל לאבחן כשהן נכשלות. E2E איטיות ויקרות — כותבים אותן רק לתהליכים קריטיים.
שאלה 2.1 · מה ההבדל בין toBe לבין toEqual?
- א. אין ביניהם כל הבדל ושניהם זהים: הנחה שגויה, אף ש-toBe בודק זהות הפניה או ערך פרימיטיבי ו-toEqual בודק שוויון עמוק של תוכן אובייקטים ומערכים
- ב. toEqual בודק זהות הפניה ו-toBe בודק ערכים: הנחה שגויה, אף שזהו ההיפך שכן toBe בודק זהות הפניה ו-toEqual בודק שוויון עמוק של תוכן בלבד
- ג. toBe בודק זהות הפניה או ערך פרימיטיבי, ואילו toEqual בודק שוויון עמוק של תוכן, ולכן שני אובייקטים שונים בזיכרון יעברו רק ב-toEqual כאן
- ד. toBe מיועד לפונקציות ו-toEqual לערכים: הנחה שגויה, אף שההבחנה היא ש-toBe בודק זהות הפניה ו-toEqual בודק שוויון עמוק של תוכן ולא לפי סוג
✓ תשובה: ג) — expect({a:1}).toBe({a:1}) נכשל כי זה שני אובייקטים שונים בזיכרון. expect({a:1}).toEqual({a:1}) עובר כי toEqual משווה את התוכן.
שאלה 2.2 · מה יהיה תוצאת הבדיקה הבאה? expect([1,2,3]).toHaveLength(2)
- א. עוברת שכן המערך מכיל את הערך 2 בתוכו: הנחה שגויה, אף ש-toHaveLength בודק את האורך שהוא 3 ולא 2 ולבדיקת הכלה של ערך משמש toContain בפועל
- ב. נזרקת שגיאה בזמן ריצה של הבדיקה: הנחה שגויה, אף שהבדיקה רצה ונכשלת שכן האורך של המערך הוא 3 ולא 2 ולא נזרקת שגיאת ריצה כלשהי במהלכה כאן
- ג. עוברת שכן toHaveLength בודק אם 2 נמצא: הנחה שגויה, אף ש-toHaveLength בודק את מאפיין האורך שהוא 3 ולא את הכלת הערך 2 בתוך המערך הנבדק
- ד. נכשלת, מכיוון ש-toHaveLength בודק את מאפיין האורך והאורך של המערך הוא 3 ולא 2, ולבדיקת הכלה של הערך 2 יש להשתמש ב-toContain בפועל כאן
✓ תשובה: ד) — toHaveLength בודק את מאפיין .length. [1,2,3].length = 3, לא 2 — הבדיקה נכשלת. לבדוק אם המערך מכיל 2 השתמש ב-toContain(2).
שאלה 3.1 · רוצים לבדוק שאחרי לחיצה על "מחק", הפריט לא קיים בDOM. איזו query מתאימה?
- א. queryByText — מחזיר null כשאין אלמנט
- ב. getByText — זורק שגיאה ומפיל את הבדיקה
- ג. findByText — ממתין להופעת אלמנט
- ד. getAllByText — זורק כשאין תוצאות
✓ תשובה: א) — queryByText מחזיר null כשהאלמנט לא קיים, ולכן: expect(screen.queryByText('פריט')).not.toBeInTheDocument(). 🎯 דגש למבחן — שאילתות RTL: רק queryBy... מחזירות null ומתאימות לבדיקת אלמנט שאינו קיים. getBy... זורק שגיאה ויכשיל את הבדיקה, ו-findBy... נועד להמתנה לאלמנט שעתיד להופיע.
שאלה 3.2 · מדוע RTL ממליצה על getByRole יותר מ-getByText?
- א. getByRole מהיר יותר מבחינת ביצועים בהרצה: הנחה שגויה, אף שהיתרון הוא ש-getByRole בודק גם נגישות ואלמנט עם role שגוי מפיל את הבדיקה בפועל
- ב. מכיוון ש-getByRole בודק גם נגישות, ולכן אלמנט עם role שגוי יגרום לבדיקה להיכשל גם אם הטקסט נכון, וזה מבטיח שהאפליקציה נגישה לכלי עזר כאן
- ג. getByText אינו עובד עם טקסט בעברית: הנחה שגויה, אף ש-getByText עובד עם עברית והיתרון של getByRole הוא בדיקת הנגישות של האלמנט הנבדק בפועל
- ד. אין ביניהם כל הבדל וזו רק העדפה אישית: הנחה שגויה, אף ש-getByRole בודק גם נגישות ואלמנט עם role שגוי מפיל את הבדיקה מעבר להעדפה כלשהי כאן
✓ תשובה: ב) — getByRole('button', {name: 'שמור'}) בודק שיש element עם role=button (ולא div עם onClick). זה מבטיח שהאפליקציה נגישה לכלים עזר (Screen Readers) ולא רק ויזואלית.
שאלה 4.1 · מה ההבדל בין getByText, queryByText ו-findByText?
- א. כולם זהים ורק התחביר שלהם שונה: הנחה שגויה, אף ש-getBy זורק כשאינו נמצא ו-queryBy מחזיר null ו-findBy ממתין באופן אסינכרוני להופעת האלמנט
- ב. findBy הוא הכי מהיר ו-getBy הכי איטי: הנחה שגויה, אף שההבחנה היא ש-getBy זורק ו-queryBy מחזיר null ו-findBy ממתין ואינה עניין של מהירות כלל
- ג. queryBy עובד רק עם CSS selectors בלבד: הנחה שגויה, אף ש-queryBy מחזיר null כשאינו נמצא וההבחנה בין השלושה היא באופן הטיפול בהיעדר תוצאה בפועל
- ד. getBy מחזיר מיד וזורק אם אינו נמצא, queryBy מחזיר null אם אינו נמצא, ו-findBy ממתין באופן אסינכרוני עד שהאלמנט מופיע במסמך בסוף בפועל כאן
✓ תשובה: ד) — שלוש queries שונות למצבים שונים: getBy לחובה, queryBy לאופציונלי, findBy לאסינכרוני. בחירת הלא-נכונה תגרום לבדיקה שנכשלת מסיבות לא נכונות.
שאלה 4.2 · למה צריך jest.clearAllMocks() ב-beforeEach?
- א. כדי למחוק את כל קבצי ה-mock מן הפרויקט: הנחה שגויה, אף שהפקודה מאפסת את היסטוריית הקריאות של ה-mock בין בדיקות ואינה מוחקת קבצים כלשהם כלל
- ב. מכיוון שפונקציית ה-mock מצברת קריאות בין בדיקות, ולכן clearAllMocks מאפס את היסטוריית הקריאות לפני כל בדיקה כדי שלא ישפיעו זו על זו כאן
- ג. מכיוון ש-Jest אינו מאפשר יותר מ-10 בדיקות: הנחה שגויה, אף שאין הגבלה כזו והפקודה מאפסת את היסטוריית הקריאות של ה-mock לפני כל בדיקה בנפרד
- ד. כדי לנקות את ה-localStorage לפני כל בדיקה: הנחה שגויה, אף שהפקודה מאפסת את היסטוריית הקריאות של ה-mock ואינה נוגעת ל-localStorage כלל בפועל
✓ תשובה: ב) — אחרי בדיקה א' שה-mock נקרא פעם אחת, בדיקה ב' תראה שהmock נקרא פעמיים (א'+ב'). clearAllMocks() מאפס את הספירה לפני כל בדיקה.
שאלה 5.1 · הפרויקט שלך מחזיר אובייקט ריק מהשרת. מה הצעד הראשון ב-Debugging?
- א. למחוק ולכתוב את הקוד מחדש מן ההתחלה: הנחה שגויה, אף שהצעד הראשון הוא בדיקת הבקשה והתשובה בלשונית ה-Network כדי לאתר את נקודת הכשל המדויקת
- ב. להוסיף console.log בכל שורת קוד בפרויקט: הנחה שגויה, אף שהצעד הראשון היעיל הוא בדיקת הבקשה והתשובה בלשונית ה-Network ולא הוספת לוג בכל שורה
- ג. לשאול את מנוע החיפוש גוגל על הבעיה: הנחה שגויה, אף שהצעד הראשון הוא בדיקת הבקשה והתשובה בלשונית ה-Network לאיתור מדויק של נקודת הכשל בפועל
- ד. לפתוח את כלי הפיתוח ואת לשונית ה-Network ולבדוק את הבקשה והתשובה האמיתיות, שכן כך מאתרים אם הכשל ב-Frontend או ב-Backend או ב-DB בפועל
✓ תשובה: ד) — Network Tab מראה בדיוק מה נשלח ומה התקבל. כך מאתרים את נקודת הכשל: האם הבעיה ב-Frontend (בקשה שגויה), ב-Backend (שגיאת שרת), או ב-DB (נתונים ריקים).
שאלה 6.1 · מדוע צריך ליצור Store חדש לכל בדיקה ב-renderWithProviders?
- א. מכיוון ש-Redux אינו מאפשר שימוש חוזר ב-Store: הנחה שגויה, אף שהוא מאפשר והסיבה היא מניעת זיהום בין בדיקות שכן Store משותף שומר נתונים ישנים
- ב. מכיוון ש-preloadedState מוחק את ה-Store הישן: הנחה שגויה, אף שהסיבה היא מניעת זיהום בין בדיקות שכן Store משותף שומר נתונים מבדיקה קודמת בפועל
- ג. מטעמי ביצועים שכן Store חדש מהיר יותר: הנחה שגויה, אף שהסיבה היא מניעת זיהום בין בדיקות שכן Store משותף שומר נתונים מבדיקה קודמת ולא מהירות
- ד. כדי למנוע זיהום בין בדיקות, שכן Store משותף יכיל נתונים מבדיקה קודמת שישפיעו על הבאה, ולכן Store חדש לכל בדיקה מבטיח בידוד מלא וידוע כאן
✓ תשובה: ד) — אם Store משותף, בדיקה א' שמוסיפה Todo תשאיר אותו לבדיקה ב'. כל בדיקה חייבת להתחיל עם State נקי וידוע. Store חדש לכל בדיקה מבטיח בידוד מלא.