Тестирањето е тежок бизнис. Тестирањето на комплетните апликации е уште потешко. Мора да се занимавате со фронт-енд, бак-енд, база на податоци, мрежа и повеќе. Прво, се разбира, ќе ги тестирате вашите компоненти, функции и модули во изолација. Потоа ќе напишете тестови за интеграција за да бидете сигурни дека тие играат убаво заедно. Можете дури и да додадете неколку тестови од крај до крај за целата апликација за да симулирате вистински интеракции со корисниците. Но, тогаш постои факторот на хаос: што се случува кога работите одат погрешно? Што се случува кога мрежата е бавна или несигурна? Што се случува кога резервниот крај е долу? Апликација која работи совршено на среќниот пат сè уште лесно може да се скрши кога се случува нешто неочекувано. Невозможно е да се предвидат сите начини на кои системот може да пропадне, особено кога повеќе компоненти комуницираат на сложени начини, но можеме да се подготвиме за неуспех со тестирање на тоа како нашата апликација се однесува под неповолни услови. Тестирањето врз основа на хаос е пристап кој ја прифаќа оваа несигурност со намерно воведување неуспеси во вашите тестови. Во оваа статија, ќе истражиме како да се имплементира тестирање врз основа на хаос во апликацијата Next.js со користење на тестови за интеграција кои намерно ги нарушуваат работите. Апликацијата За да ја задржиме статијата фокусирана, создадов минимална целосна Next.js апликација за да не морате. Апликацијата е едноставна апликација за рецепти каде што можете да пребарувате низ листа на рецепти, да ги прегледате деталите за рецепти и како нив. Кога ви се допаѓа рецепт, сличното броење оптимистички се ажурира на предниот крај, додека задниот крај го обработува барањето. Ако повикот на задниот крај не успее, сличното броење се враќа во претходната состојба. Ако успее, враќа новото слично броење, но предниот крај не го ажурира повторно за да избегне скокање броеви ако рецептот во меѓувреме му се допаднал на друг корисник. Ова е вообичаен модел во веб апликациите за да обезбеди брзо корисничко искуство кога конзистентноста не е критична. Кодот е достапен на . GitHub Проверете го репото, инсталирајте зависности и можете да го покренете локално со: git clone cd article-chaos-fetch npm install npm run dev Отворено во вашиот прелистувач. http://localhost:3000 Ќе видите нешто како ова: Бацкендот има три 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 Тестирање на единиците со работник од областа на услугата (MSW) Ако сакате да го тестирате вашиот дел, постојат неколку начини да го направите тоа, но еден од најпаметните начини е да го користите На овој начин, можете да го тестирате компонентата во изолација без да се потпирате на вистинскиот позадина. Службен работник на мок (MSW) Во на Бранд, ние се воспостави Како тест тркач и Ние исто така го поставивме MSW за да ги пресретнуваме барањата од мрежата и да ги враќаме одговорите на измамите. main Брзо React Тестирање библиотека содржи единиците за тестирање за компонента. Тој ги тестира следниве сценарија: src/components/LikeButton.test.tsx LikeButton Како копчето го оневозможува за време на барањето и ажурира на вредноста за поддршка на успехот. Како копчето се оневозможува за време на барањето и се враќа на грешка во позадина. Можете да ги извршите тестовите со: 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 го прави е да ги пресретнува мрежните барања направени од На овој начин, можеме да тестираме како компонентата се однесува во различни услови без да се потпираме на вистинскиот задник. Сепак, не е вклучен вистински задник, па не можеме да ја тестираме целосната интеграција помеѓу предниот и задниот крај! LikeButton Тестови за интеграција Значи, што можеме да направиме за да ја тестираме целосната интеграција помеѓу фронтендот и бакендот? Технички, можеме да го смениме MSW за да ги пренесеме барањата на вистинскиот бакенд, но тоа би било малку хакирано и не навистина за што е дизајниран MSW. или да извршувате тестови од крај до крај и да користите самостоен прокси како Или нешто поедноставно како да ги симулирате условите на мрежата - но тоа би било малку претерано за оваа едноставна апликација. Играч Кипар Токсикоза Хаос-прокси Ова е местото каде Тоа е лесна библиотека која го обвива домородниот народ. API и ви овозможува да воведете хаос во мрежните барања.Можете да симулирате латентност, грешки, ограничување на стапката, throttling, па дури и случајни неуспеси со само неколку линии на код. Хаос фетиш fetch Ајде да користиме Ако сакаме да ја тестираме целосната интеграција помеѓу фронт-ендот и бакендот, треба да ги извршиме тестовите во вистинска прелистувачка средина. за ова, што симулира прелистувач-како средина во 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 ... Како што можете да видите, ние создаваме Клиентот кој, наместо да го преземе, враќа некои прикриени податоци и го заменува глобалниот функционира со него. во Hook, ние го враќаме оригиналот Остатокот од кодот за тестирање останува ист. 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 Тестирање на хаотична интеграција Ако сакаме да се движиме подалеку од тестовите на единиците и да ја тестираме целосната интеграција помеѓу фронт-енд и бак-енд, можеме да користиме да воведеме хаос во нашите мрежни барања.На овој начин, можеме да тестираме како апликацијата се однесува во неповолни услови. chaos-fetch Прво, за тестовите за интеграција мораме да направиме мал рефактор: не можеме директно да репродуцираме асинхронна серверска компонента во нашите тестови. Компонента која го содржи и рецепт детали. на овој начин, ние може да се тестира во нашите тестови за интеграција. кодот за е во . PostPage LikeButton LikeButton PostView src/components/PostView.tsx Во нашиот случај, тоа е целата апликација, што ја прави конфигурацијата смешна затоа што ќе тестираме компонента против апликацијата од која е дел, но можете да поставите тестови за интеграција на ист начин ако backend е посебна услуга. Истите тестови што ги имавме во Повторно се запишани во Кодот е сличен, но наместо да се шегуваме со одговорите на задната страна, дозволуваме барањето да помине на вистинскиот задната страна. Внесете грешки во барањата. LikeButton.test.tsx PostView.integration.test.tsx chaos-fetch Важен аспект е тоа што треба да се постави на URL објект, така што може правилно да ги реши релативните URL адреси. Во оваа конфигурација, JSDOM со native fetch, дури и не би работеле за релативните URL адреси! Патеки на JSDOM Да го направат да работи. globalThis.location chaos-fetch chaos-fetch location Прво, ние поставивме : globalThis.location globalThis.location = new URL("http://localhost:3000/posts/1"); Потоа создаваме клиентите во тестовите кои го надминуваат native pick, и можеме да инјектираме латенција, грешки и повеќе. 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(); }); Сега, технички, во вториот тест, ние воопшто не го нарекуваме позадината, бидејќи Презема барање и враќа грешка, но дава единствен интерфејс за справување со успешни и неуспешни барања. chaos-fetch Каде овој пристап навистина блесне е кога сакате да симулирате повеќе сложени услови на мрежата. На пример, можете да симулирате бавни мрежи со Или можете да пробате што се случува ако вашиот задник ви го ограничува стапката со Средношколците throttle rateLimit Друга работа што е тешко да се тестира е дали се прикажува задоцнет ротатор за полнење додека барањето е во лет. middleware за симулација на бавна мрежа и тестирање дека вашата состојба на полнење е правилно прикажана. latency Ако не ви е гајле за детерминизмот, можете дури и да додадете некои случајни неуспеси за да видите како вашата апликација се однесува под непредвидливи услови. Заклучок Тестирањето на хаосот често се поврзува со големи дистрибуирани системи, но е подеднакво важно за помалите апликации.Во оваа статија, ги истражувавме лесните тестови на хаосот за апликации со целосен пакет со користење на тестови за интеграција кои намерно ги кршат работите. да воведеме хаос во нашите мрежни барања и да тестираме како нашата апликација се однесува во неповолни услови. chaos-fetch не е подобра (или полоша) замена за MSW или рамки за тестирање од крај до крај како Playwright или Cypress. Со прифаќање на хаосот и тестирање на тоа како вашата апликација се однесува во услови на неуспех, можете да изградите повеќе отпорни и робусни апликации. @fetchkit/chaos-fetch @fetchkit/chaos-proxy