Crash ב-Production, ולמה Testing ב-Mobile שונה מהכל
באפריל 2025 פרסמו ב-Reddit: "We had 99% unit test coverage and still shipped a bug that crashed the app on Android 12 for 40% of our users. Coverage is not safety." זה לא סיפור על חוסר מאמץ, זה סיפור על testing בצורה הלא נכונה.
Crash ב-Web = error בconsole. Crash ב-mobile app עם 200,000 משתמשים ישראליים = ביקורות 1-כוכב ב-App Store, מייל חירום ל-QA, ו-hotfix שמחייב App Store review נוסף, 24-72 שעות עיכוב. Testing ב-React Native שומר אתכם מהתרחיש הזה, אבל רק אם תבדקו את הדברים הנכונים בצורה הנכונה.
יש שלושה layers לtesting ב-React Native, ולכל אחד תפקיד שונה: Unit tests עם Jest ללוגיקה עסקית, Component tests עם React Native Testing Library (RNTL) ל-UI behavior, ו-E2E tests עם Detox או Maestro לflow מלא. Claude 4.x כותב את שלושתם, אבל הפרומפט חייב להיות מדויק.
Layer 1: Unit Tests עם Jest, לוגיקה שאין לה אמון עיוור
Unit tests לא בודקים מה המשתמש רואה, הם בודקים שהחישוב נכון. Cart store, חישוב מחיר, פונקציית validation, hook של fetching, כולם מועמדים. הטריק: Jest רץ ב-Node.js, לא על מכשיר. לכן native modules כמו MMKV (C++ binary) ו-AsyncStorage לא קיימים בסביבה זו, חייבים לעטוף אותם ב-mock.
כך תבקשו מ-Claude unit tests שבאמת מגנים:
שימו לב: הפרומפט מגדיר 6 קייסים ספציפיים, מציין את ה-mock הנדרש, ומבקש reset בין tests. בלי כל אחד מאלה, Claude ייצר tests שרצים, אבל לא בהכרח בודקים את מה שצריך.
Layer 2: RNTL, מה המשתמש רואה, לא מה הקוד עושה
הטעות הנפוצה ביותר עם React Native Testing Library היא לבדוק implementation: expect(wrapper.state.isLoading).toBe(true), זה נשבר בכל refactor שלא משנה שום behavior. הגישה הנכונה: בדוק מה המשתמש חווה.
- במקום:
wrapper.state.isLoading, השתמשו ב:expect(screen.getByTestId('loading-indicator')).toBeTruthy() - במקום:
component.props.onPress, השתמשו ב:fireEvent.press(screen.getByText('הוסף לcart')) - במקום: snapshot של כל ה-component, בדקו שהtext הנכון מופיע ושה-interaction עובדת
Snapshot testing הוא בעיה: כפי שנכתב בבלוג EZCater הנפוץ, "When a snapshot fails, it is tempting to update it without investigating whether the change is expected." זה הופך לcomfort theater, tests שעוברים אוטומטית ולא מגנים על כלום.
Layer 3: E2E, Detox vs Maestro (2025)
נכון ל-2025, יש שתי אפשרויות עיקריות ל-E2E testing ב-React Native:
| קריטריון | Detox | Maestro |
|---|---|---|
| גישה | Grey-box, מסתנכרן עם JS thread | Black-box, accessibility layer |
| Flakiness | פחות מ-2% | פחות מ-1% |
| Setup | מורכב, native config נדרש | YAML פשוט, אפס שינויי native |
| מתאים ל | Programmatic mocking, dev teams | QA teams, CI מהיר, onboarding קל |
| שפה | JavaScript / TypeScript | YAML |
לרוב הפרויקטים ב-2025: Maestro לflows פשוטים וCI, Detox לflows מורכבים עם mocking מתקדם. אם אתם מתחילים פרויקט חדש ואין לכם QA מנוסה עם Detox, Maestro יחסוך לכם שבוע setup.
אסטרטגיית CI, מה מריצים מתי
לא כל test רץ בכל PR. זו אסטרטגיה שחוסכת זמן CI:
- בכל PR: Unit tests + RNTL component tests (מהירים, שניות)
- בmerge ל-main: E2E Detox/Maestro על simulator (דקות)
- Nightly: E2E על real devices ב-AWS Device Farm או BrowserStack
Coverage thresholds ב-jest.config.js: אל תרדפו אחרי 100%. הגדירו 80% statements, 75% branches לכל הproject, ו-95% לpaths קריטיים: checkout, auth, cart. בסטארטאפ תל-אביבי שעובד על אפליקציית B2B, הקריטי הוא flow ההזמנה, לא ה-helper של date formatting.
שלוש טעויות שעולות בProduction
- לא מאפסים state בין tests: Zustand עם persist ממשיך בין tests ב-suite. תמיד
beforeEach(() => useCartStore.setState(initialState)). בלי זה, tests עוברים כשמריצים בודד ונכשלים ב-CI כי הסדר שונה. - E2E מכה ב-real API: כשהbackend מחזיר נתונים אחרים מיום ליום, ה-test נכשל לא בגלל bug, אלא בגלל data. תמיד
launchArgs: { mockApi: 'true' }וmock שמחזיר products ידועים. - אין mock לnative modules: jest.setup.js חייב לכלול stubs לMMKV, AsyncStorage, NetInfo. בלי זה, CI נכשל עם
Cannot find module 'react-native-mmkv', כי Jest רץ ב-Node.js ואין שם C++ binary.
סיכום, Testing Pyramid שעובד
הפרמידה הנכונה לפרויקט React Native: 60% unit tests (Jest), 30% component tests (RNTL), 10% E2E (Detox/Maestro). Coverage 80% ל-project, 95% לpaths קריטיים כמו checkout ו-auth. בCI: unit+component בכל PR, E2E בmerge בלבד.
Claude כותב את שלושת הlayers, אבל תנו לו context: הקוד, המודלים, ה-stores, ורשימת הmocks הנדרשים. תוצאה: test suite שמגן, לא מרשים.
