בדיקה היא עסק מסובך. בדיקה של אפליקציות מלאות היא אפילו יותר מסובכת. אתה צריך להתמודד עם frontend, backend, מסד נתונים, רשת ועוד. ראשית, כמובן, אתה בודד בדיקות הרכיבים שלך, פונקציות, ומודולים בנפרד. לאחר מכן אתה כותב בדיקות אינטגרציה כדי לוודא שהם משחקים היטב יחד. אתה יכול אפילו להוסיף כמה בדיקות end-to-end עבור היישום כולו כדי לדמות אינטראקציות משתמשים אמיתיות. אבל אז יש הגורם לכאוס: מה קורה כשדברים הולכים לא בסדר? מה קורה כאשר הרשת איטית או לא אמין? מה קורה כאשר האחורי נופל? יישום שעובד בצורה מושלמת על הדרך המאושרת עדיין יכול בקלות לשבור כאשר קורה משהו בלתי צפוי. אי אפשר לחזות את כל הדרכים שבהן מערכת יכולה להיכשל, במיוחד כאשר מרכיבים מרובים אינטראקציה בדרכים מורכבות, אבל אנחנו יכולים להתכונן להיכשל על ידי בדיקות כיצד היישום שלנו מתנהג בתנאים שליליים. בדיקה מבוססת על כאוס היא גישה שמטפלת בחוסר ודאות זו על ידי הצגת כישלונות במכוון במבחנים שלך. במאמר זה, נחקור כיצד ליישם בדיקות מבוססות על כאוס ביישום Next.js באמצעות בדיקות אינטגרציה שמטרתן לשבור דברים. האפליקציה כדי לשמור על המאמר ממוקד, יצרתי אפליקציית Next.js מינימלית מלאה, כך שאתה לא צריך. האפליקציה היא יישום מתכון פשוט שבו אתה יכול לחפש רשימה של מתכונים, להציג את הפרטים של מתכון, כמו גם אותם. כאשר אתה אוהב מתכון, כמו מספר מתעדכן באופן אופטימי על החזית, בעוד האחורי מעבד את הבקשה. אם שיחת האחורית נכשלת, כמו מספר חוזר למצב הקודם. אם זה מצליח, זה מחזיר את כמו מספר חדש, אבל החזית לא מעודכן אותו שוב כדי למנוע קפיצות מספרים אם המתכון היה מוצא חן בעיניי. הקוד זמין ב . Github בדוק את repo, להתקין תלויות, ואתה יכול להפעיל אותו באופן מקומי עם: git clone cd article-chaos-fetch npm install npm run dev פתוחה בתוך הדפדפן שלך http://localhost:3000 אתם תראו משהו כזה: ל-Backend יש שלושה מסלולי API: GET /api/posts — רשימה של כל המתכונים GET /api/posts/[id] — קבל פרטים על המתכון POST /api/posts/[id]/like — Increment likes - Returns the new like count שים לב כי אהובים מאוחסנים בזיכרון ומוגדרים מחדש בעת הפעלת מחדש של השרת.זה רק למטרות הדגמה; באפליקציה אמיתית, היית משתמש במסד נתונים. כפתור זה מופיע ב שימוש ב- React ו זה מתמודד עם עדכון אופטימי, ניהול שגיאות, ו לוגיקה הפוך. כנראה לא איך אתה או אני היינו ליישם אותו באפליקציה אמיתית, אבל זה יעשה עבור הדומיין הזה. src/components/LikeButton.tsx useState useEffect בדיקת יחידות עם עובד שירות Mock (MSW) אם אתה רוצה לבדוק את הרכיב שלך, ישנן דרכים רבות לעשות את זה, אבל אחת הדרכים החכמות ביותר היא להשתמש בדרך זו, אתה יכול לבדוק את הרכיב בבידוד מבלי להסתמך על הרכיב האמיתי. עובד שירות Mock (MSW) ב The הברזילאי, אנחנו יוצרים כמו בדיקת ריצה ו כדי להציג את הרכיב ולסימולציה של אינטראקציות משתמשים, הגדרנו גם MSW כדי לעקוף את בקשות הרשת ולהחזיר תשובות מזויפות. main מהירות ספריות React Testing מכיל את הבדיקות היחידות עבור התוכנית מבצעת את התסריטים הבאים: src/components/LikeButton.test.tsx LikeButton כפתור דומה משבית במהלך הבקשה ומעדכן את הערך האחורי על ההצלחה. כפתור דומה משבית במהלך הבקשה ומגלגל בחזרה על שגיאת backend. אתה יכול לבצע את הבדיקות עם: npm run test אם נסתכל על קוד הבדיקה, נוכל לראות כיצד אנו משתמשים ב- MSW כדי לבלבל את התשובות האחוריות.לדוגמה, במבחן הראשון, אנו מורידים את הבדיקה כדי להחזיר תשובה מוצלחת עם מספר כמו חדש של 42: // test("like button disables during request and updates to backend value" ... server.use( http.post("/api/posts/:id/like", async () => { await new Promise((resolve) => setTimeout(resolve, 100)); return { likes: 42 }; }) ); ... במבחן השני, אנו מורידים את הזיוף כדי להחזיר תשובה שגיאה: test("like button disables during request and rolls back on backend error" ... server.use( http.post("/api/posts/:id/like", async () => { await new Promise((resolve) => setTimeout(resolve, 100)); return { status: 500 }; }) ); ... מה MSW עושה הוא לעקוף את בקשות הרשת שנעשו על ידי כך, אנו יכולים לבדוק כיצד התוכן מתנהג בתנאים שונים של backend מבלי להסתמך על backend בפועל, עם זאת, אין backend אמיתי מעורב, ולכן אנחנו לא יכולים לבדוק את האינטגרציה המלאה בין frontend לבין backend! LikeButton בדיקות אינטגרציה אז, מה אנחנו יכולים לעשות כדי לבדוק את האינטגרציה המלאה בין frontend לבין backend? טכנית, אנחנו יכולים לשנות את MSW כדי להעביר את הבקשות אל backend בפועל, אבל זה יהיה קצת הוקי ולא באמת מה MSW מיועד. או כדי להפעיל בדיקות מסוף לקצה ולהשתמש ב- proxy עצמאי כמו או משהו יותר פשוט כמו כדי לסמול את תנאי הרשת - אבל זה יהיה קצת overkill עבור יישום פשוט זה. Playwright Cypress toxiproxy תגית: proxy זה המקום שבו זהו ספרייה קלה המקיפה את הילידים. API ומאפשר לך להכניס כאוס לבקשות הרשת שלך.אתה יכול לסמול את האינטנסיביות, שגיאות, הגבלת קצב, ג'וטלינג, ואפילו כישלונות אקראיים עם רק כמה שורות של קוד. תגית: chaos fetch fetch בואו נשתמש כדי ליצור כמה בדיקות אינטגרציה.אם אנחנו רוצים לבדוק את האינטגרציה המלאה בין frontend לבין backend, אנחנו צריכים להפעיל את הבדיקות בסביבה דפדפן אמיתית. הסביבה עבור זה, אשר מדמיין סביבה דומה לדפדפן ב Node.js. chaos-fetch jsdom To use `chaos-fetch`, we first need to install it: ```bash npm install @fetchkit/chaos-fetch --save-dev הדבר הראשון שאנחנו יכולים לעשות, למטרות איור, הוא להחליף MSW עם זה לא באמת למה הספרייה מיועדת, אבל זה עובד. אנו מחליפים את ה- MSW עם : chaos-fetch LikeButton.test,tsx chaos-fetch // src/components/LikeButton.test.tsx import { createClient, replaceGlobalFetch, restoreGlobalFetch, } from "@fetchkit/chaos-fetch"; ... describe("LikeButton", () => { afterEach(() => { restoreGlobalFetch(); }); test("like button disables during request and updates to backend value", async () => { // Mock fetch to return success const client = createClient( { global: [ { latency: { ms: 300 } }, ], routes: { "POST /api/posts/:id/like": [ { latency: { ms: 300 } }, { mock: { body: '{ "likes": 43 }' } }, ], }, }, window.fetch ); // Replace global fetch with mock client replaceGlobalFetch(client); // From here on, the test code remains the same ... כפי שאתם יכולים לראות, אנו יוצרים לקוח שממנו, במקום לקבל, מחזיר נתונים מזויפים ומחליף את הנתונים הגלובליים. זה עובד עם זה, ב הוק, אנחנו מחזירים את המקורי שאר הקוד של הבדיקה נשאר זהה. chaos-fetch fetch afterEach fetch שים לב, הוספנו גם איזושהי עיכוב כדי לעקוב אחר בקשת רשת אמיתית. הרכיב מפעיל את הכפתור במהלך הבקשה, ואנחנו רוצים לבדוק את ההתנהגות הזאת.בלי זה, הבדיקה לא תצליח כי הבדיקה תסתיים מהר מדי. (זה מוביל אותנו לעולם הרך, לא אמין של בדיקות מבוססות זמן, אבל זה נושא עבור מאמר אחר.) LikeButton הקוד עבור הבדיקה השנייה דומה; אנחנו פשוט משנים את המראה כדי להחזיר שגיאה: test("like button disables during request and rolls back on backend error", async () => { // Mock fetch to return success const client = createClient( { global: [], routes: { "POST /api/posts/:id/like": [ { latency: { ms: 300 } }, { mock: { status: 500, body: '{ "error": "Internal Server Error" }' } }, ], }, }, window.fetch ); // Replace global fetch with mock client replaceGlobalFetch(client); ... הקוד נמצא על תגיות קשורות תגיות קשורות תגיות קשורות תגיות קשורות תגיות קשורות . tests-with-chaos-fetch git checkout tests-with-chaos-fetch עכשיו, אנחנו יכולים להפעיל את הבדיקות עם: npm run test עכשיו, MSW הוא עדיין מתאים יותר עבור בדיקות יחידה, כי זה נועד למטרה זו. גם בשביל זה. chaos-fetch בדיקת אינטגרציה מבוססת כאוס אם אנחנו רוצים לעבור מעבר לבדיקות יחידות ולבדוק את האינטגרציה המלאה בין frontend לבין backend, אנחנו יכולים להשתמש כדי להכניס כאוס לבקשות הרשת שלנו, אנו יכולים לבדוק כיצד יישום מתנהג בתנאים שליליים. chaos-fetch ראשית, עבור הבדיקות של אינטגרציה, אנחנו צריכים לעשות refactor קטן: אנחנו לא יכולים ישירות להציג רכיב שרת אסונק בבדיקות שלנו. המרכיב המכיל את תגיות: תגיות: כך נוכל לבדוק בדיקת אינטגרציה: הקוד הוא ב . PostPage LikeButton LikeButton PostView src/components/PostView.tsx במקרה שלנו, זה האפליקציה כולה, מה שהופך את ההתקנה מצחיקה בגלל שאנחנו נבדוק רכיב נגד האפליקציה שהוא חלק ממנה, אבל אתה יכול להגדיר בדיקות אינטגרציה באותו אופן אם האחורי הוא שירות נפרד. באותם בדיקות שהיו לנו הם נכתבו מחדש כדי לבדוק את האינטגרציה המלאה בין frontend לבין backend.הקוד דומה, אבל במקום לצחוק את התשובות של backend, אנו מאפשרים לבקשות לעבור אל backend בפועל. להכניס טעויות לבקשות. LikeButton.test.tsx PostView.integration.test.tsx chaos-fetch הדבר החשוב ביותר הוא שאנחנו צריכים להגדיר כדי להגדיר אובייקט URL, כך ניתן לפתור כתובות URL יחסיות כראוי.בהתקנה זו, JSDOM עם ה-native fetch, לא יעבוד אפילו עבור כתובות URL יחסיות! תגיותJSDOM's כדי לגרום לזה לעבוד. globalThis.location chaos-fetch chaos-fetch location קודם כל, אנחנו קובעים : globalThis.location globalThis.location = new URL("http://localhost:3000/posts/1"); לאחר מכן יצרנו הלקוחות במבחנים שמחליפים את הרכישה המקורית, ואנחנו יכולים להדביק עיכוב, שגיאות ועוד. chaos-fetch עבור הבדיקה הראשונה, אנחנו אפילו לא צריך לשנות אנחנו רק מורידים אותו כך שהוא עובד עם כתובות URL יחסיות: fetch test("integration: like button disables during request and re-enables after fetch (real backend)", async () => { replaceGlobalFetch(createClient({})); render(<PostView postId={1} />); // Wait for post to load (like count should be present) const likeCountText = await screen.findByText(/\d+\s*likes/); const initialCount = Number(likeCountText.textContent.match(/(\d+)/)?.[1] ?? 0); const button = await screen.findByRole("button", { name: /like/i }); const user = userEvent.setup(); await user.click(button); // Button should be disabled during request expect(button).toBeDisabled(); // Wait for fetch to complete and UI to update await waitFor(() => expect(button).not.toBeDisabled()); // Check updated like count await waitFor(() => { const updatedLikeCountText = screen.getByText(/\d+\s*likes/); const updatedCount = Number(updatedLikeCountText.textContent.match(/(\d+)/)?.[1] ?? 0); expect(updatedCount).toBe(initialCount + 1); }); restoreGlobalFetch(); }); עבור הבדיקה השנייה, אנו יוצרים לקוח שמדמיין שגיאה באקנד עבור בקשה דומה: test("integration: like button disables during request and rolls back on backend error (fail middleware)", async () => { // Configure chaos-fetch to fail the like endpoint replaceGlobalFetch(createClient({ routes: { "POST /api/posts/:id/like": [ { latency: { ms: 300 } }, { fail: { status: 500, body: '{ "error": "fail middleware" }' } }, ], }, })); render(<PostView postId={1} />); // Wait for post to load (like count should be present) const likeCountText = await screen.findByText(/\d+\s*likes/); const initialCount = Number(likeCountText.textContent.match(/(\d+)/)?.[1] ?? 0); const button = await screen.findByRole("button", { name: /like/i }); const user = userEvent.setup(); await user.click(button); // Button should be disabled during request expect(button).toBeDisabled(); // Wait for fetch to complete and UI to update await waitFor(() => expect(button).not.toBeDisabled()); // Check that like count rolls back to original value await waitFor(() => { const rolledBackLikeCountText = screen.getByText(/\d+\s*likes/); const rolledBackCount = Number(rolledBackLikeCountText.textContent.match(/(\d+)/)?.[1] ?? 0); expect(rolledBackCount).toBe(initialCount); }); restoreGlobalFetch(); }); עכשיו, מבחינה טכנית, בבדיקה השנייה, אנחנו לא קוראים ל-backend בכלל, כי הוא חותם את הבקשה ומחזיר שגיאה, אבל הוא נותן ממשק מאוחד כדי להתמודד עם בקשות מוצלחות ובלתי מוצלחות. chaos-fetch היכן גישה זו באמת זורחת היא כאשר אתה רוצה לסמול את התנאים הרשת המורכבים יותר. או שאתה יכול לנסות מה קורה אם backend שלך הוא קצב-גביל אותך עם Middleware כללי throttle rateLimit דבר נוסף שקשה לבדוק הוא אם מופיע ספינר טעינה מאוחרת בזמן שהבקשה נמצאת בטיסה. middleware כדי לסמולר רשת איטית ולבדוק כי מצב הטעינה שלך מוצג כראוי. latency אם אתה לא אכפת דטרמיניזם, אתה יכול אפילו להוסיף כמה כישלונות אקראיים כדי לראות איך האפליקציה שלך מתנהגת בתנאים לא צפויים. מסקנה בדיקת כאוס קשורה לעתים קרובות למערכות מפוזרות בקנה מידה גדול, אך היא חשובה באותה מידה עבור יישומים קטנים יותר. במאמר זה, חקרנו בדיקת כאוס קלה עבור אפליקציות מלאות באמצעות בדיקת אינטגרציה שמשפיעה בכוונה על דברים. להכניס כאוס לבקשות הרשת שלנו ולבדוק כיצד היישום שלנו מתנהג בתנאים שליליים. chaos-fetch זה לא תחליף טוב יותר (או גרוע יותר) עבור MSW או מסגרות בדיקה של סוף סוף כמו Playwright או Cypress. על ידי קבלת כאוס ובדיקת איך היישום שלך מתנהג בתנאים של כישלון, אתה יכול לבנות יישומים חזקים יותר. @fetchkit/chaos-fetch @fetchkit/chaos-proxy