国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

目錄
開始進行 React 測試
選項 1:單元測試
選項 2:集成測試
那么,什么 需要 單元測試?
其他好處
清晰的 waitFor 塊
行內(nèi) it 注釋
團隊的后續(xù)步驟
首頁 web前端 css教程 反應集成測試:覆蓋范圍更大,測試較少

反應集成測試:覆蓋范圍更大,測試較少

Apr 07, 2025 am 09:20 AM

React Integration Testing: Greater Coverage, Fewer Tests

對于像使用 React 構(gòu)建的交互式網(wǎng)站,集成測試是自然而然的選擇。它們驗證用戶與應用程序的交互方式,而無需端到端測試的額外開銷。

本文通過一個練習來闡述,該練習從一個簡單的網(wǎng)站開始,使用單元測試和集成測試驗證行為,并演示集成測試如何通過更少的代碼行實現(xiàn)更大的價值。本文內(nèi)容假設您熟悉 React 和 JavaScript 中的測試。熟悉 Jest 和 React Testing Library 會有所幫助,但不是必需的。

測試分為三種類型:

  • 單元測試獨立驗證一段代碼。它們易于編寫,但可能會忽略大局。
  • 端到端測試 (E2E) 使用自動化框架(例如 Cypress 或 Selenium)像用戶一樣與您的網(wǎng)站交互:加載頁面、填寫表單、點擊按鈕等。它們通常編寫和運行速度較慢,但與真實的 用戶體驗非常接近。
  • 集成測試介于兩者之間。它們驗證應用程序的多個單元如何協(xié)同工作,但比 E2E 測試更輕量級。例如,Jest 自帶一些內(nèi)置實用程序來促進集成測試;Jest 在后臺使用 jsdom 來模擬常見的瀏覽器 API,其開銷小于自動化,并且其強大的模擬工具可以模擬外部 API 調(diào)用。

另一個需要注意的地方:在 React 應用程序中,單元測試和集成測試的 編寫方式相同,使用的工具也相同。

開始進行 React 測試

我創(chuàng)建了一個簡單的 React 應用程序(可在 GitHub 上找到),其中包含一個登錄表單。我將其連接到 reqres.in,這是一個我發(fā)現(xiàn)用于測試前端項目的方便的 API。

您可以成功登錄:

…或者遇到來自 API 的錯誤消息:

代碼結(jié)構(gòu)如下:

<code>LoginModule/
├── components/
│   ├── Login.js // 渲染 LoginForm、錯誤消息和登錄確認
│   └── LoginForm.js // 渲染登錄表單字段和按鈕
├── hooks/
│    └── useLogin.js // 連接到 API 并管理狀態(tài)
└── index.js // 將所有內(nèi)容整合在一起</code>

選項 1:單元測試

如果您像我一樣喜歡編寫測試——也許戴著耳機,在 Spotify 上播放著不錯的音樂——那么您可能會忍不住為每個文件編寫單元測試。

即使您不是測試愛好者,您也可能正在參與一個“試圖做好測試”的項目,但沒有明確的策略,測試方法是“我想每個文件都應該有自己的測試?”

這看起來像這樣(為了清晰起見,我在測試文件名中添加了 unit):

<code>LoginModule/
├── components/
│ ? ├── Login.js
│ ? ├── Login.unit.test.js
│ ? ├── LoginForm.js
│ ? └── LoginForm.unit.test.js
├── hooks/
│ ? ├── useLogin.js?
│ ? └──?useLogin.unit.test.js
├── index.js
└── index.unit.test.js</code>

我在 GitHub 上完成了添加所有這些單元測試的練習,并創(chuàng)建了一個 test:coverage:unit 腳本以生成覆蓋率報告(Jest 的內(nèi)置功能)。我們可以通過四個單元測試文件實現(xiàn) 100% 的覆蓋率:

100% 的覆蓋率通常是過度的,但對于如此簡單的代碼庫來說是可以實現(xiàn)的。

讓我們深入研究為 onLogin React hook 創(chuàng)建的單元測試之一。如果您不熟悉 React hook 或如何測試它們,請不要擔心。

test('successful login flow', async () => {
? // 模擬成功的 API 響應
? jest
? ? .spyOn(window, 'fetch')
? ? .mockResolvedValue({ json: () => ({ token: '123' }) });

? const { result, waitForNextUpdate } = renderHook(() => useLogin());

? act(() => {
? ? result.current.onSubmit({
? ? ? email: '[email?protected]',
? ? ? password: 'password',
? ? });
? });

? // 將狀態(tài)設置為 pending
? expect(result.current.state).toEqual({
? ? status: 'pending',
? ? user: null,
? ? error: null,
? });

? await waitForNextUpdate();

? // 將狀態(tài)設置為 resolved,存儲電子郵件地址
? expect(result.current.state).toEqual({
? ? status: 'resolved',
? ? user: {
? ? ? email: '[email?protected]',
? ? },
? ? error: null,
? });
});

這個測試寫起來很有趣(因為 React Hooks Testing Library 使測試 hook 變得輕而易舉),但它有一些問題。

首先,測試驗證內(nèi)部狀態(tài)從 'pending' 更改為 'resolved';此實現(xiàn)細節(jié)不會向用戶公開,因此,可能不是要測試的好東西。如果我們重構(gòu)應用程序,我們將不得不更新此測試,即使從用戶的角度來看沒有任何變化。

此外,作為單元測試,這只是其中一部分。如果我們想驗證登錄流程的其他功能,例如提交按鈕文本更改為“加載中”,我們將不得不在不同的測試文件中進行操作。

選項 2:集成測試

讓我們考慮添加一個集成測試來驗證此流程的替代方法:

<code>LoginModule/
├── components/
│ ? ├── Login.js
│ ? └── LoginForm.js
├── hooks/
│  ?└── useLogin.js?
├── index.js
└── index.integration.test.js</code>

我實現(xiàn)了這個測試和一個 test:coverage:integration 腳本以生成覆蓋率報告。就像單元測試一樣,我們可以達到 100% 的覆蓋率,但這次都在一個文件中,并且需要的代碼行更少。

以下是涵蓋成功登錄流程的集成測試:

test('successful login', async () => {
  jest
    .spyOn(window, 'fetch')
    .mockResolvedValue({ json: () => ({ token: '123' }) });

  render(<loginmodule></loginmodule>);

  const emailField = screen.getByRole('textbox', { name: 'Email' });
  const passwordField = screen.getByLabelText('Password');
  const button = screen.getByRole('button');

  // 填寫并提交表單
  fireEvent.change(emailField, { target: { value: '[email?protected]' } });
  fireEvent.change(passwordField, { target: { value: 'password' } });
  fireEvent.click(button);

  // 它設置加載狀態(tài)
  expect(button).toBeDisabled();
  expect(button).toHaveTextContent('Loading...');

  await waitFor(() => {
    // 它隱藏表單元素
    expect(button).not.toBeInTheDocument();
    expect(emailField).not.toBeInTheDocument();
    expect(passwordField).not.toBeInTheDocument();

    // 它顯示成功文本和電子郵件地址
    const loggedInText = screen.getByText('Logged in as');
    expect(loggedInText).toBeInTheDocument();
    const emailAddressText = screen.getByText('[email?protected]');
    expect(emailAddressText).toBeInTheDocument();
  });
});

我真的很喜歡這個測試,因為它從用戶的角度驗證了整個登錄流程:表單、加載狀態(tài)和成功確認消息。集成測試非常適合 React 應用程序,正是因為這種用例;用戶體驗是我們想要測試的 內(nèi)容,而這幾乎總是涉及 多個不同的代碼片段協(xié)同工作。

此測試不了解使預期行為起作用的組件或 hook,這很好。只要用戶體驗保持不變,我們就可以重寫和重構(gòu)這些實現(xiàn)細節(jié)而不會破壞測試。

我不會深入研究登錄流程的初始狀態(tài)和錯誤處理的其他集成測試,但我鼓勵您在 GitHub 上查看它們。

那么,什么 需要 單元測試?

與其考慮單元測試與集成測試,不如讓我們退一步,考慮一下我們?nèi)绾螞Q定首先需要測試什么。需要測試 LoginModule,因為它是一個我們希望使用者(應用程序中的其他文件)能夠放心地使用的實體。

另一方面,不需要測試 onLogin hook,因為它只是 LoginModule 的實現(xiàn)細節(jié)。但是,如果我們的需求發(fā)生變化,并且 onLogin 在其他地方有用例,那么我們將需要添加我們自己的(單元)測試來驗證其作為可重用實用程序的功能。(我們也需要移動該文件,因為它不再特定于 LoginModule 了。)

單元測試仍然有很多用例,例如需要驗證可重用選擇器、hook 和普通函數(shù)。在開發(fā)代碼時,您可能還會發(fā)現(xiàn)使用單元測試進行 測試驅(qū)動開發(fā) 很有幫助,即使您稍后將該邏輯向上移動到集成測試。

此外,單元測試在針對多個輸入和用例進行詳盡測試方面做得很好。例如,如果我的表單需要針對各種場景(例如無效電子郵件、缺少密碼、密碼過短)顯示內(nèi)聯(lián)驗證,我將在集成測試中涵蓋一個代表性案例,然后在單元測試中深入研究具體案例。

其他好處

既然我們在這里,我想談談一些幫助我的集成測試保持清晰和有序的語法技巧。

清晰的 waitFor 塊

我們的測試需要考慮 LoginModule 的加載狀態(tài)和成功狀態(tài)之間的延遲:

const button = screen.getByRole('button');
fireEvent.click(button);

expect(button).not.toBeInTheDocument(); // 太快了,按鈕還在!

我們可以使用 DOM Testing Library 的 waitFor 輔助函數(shù)來做到這一點:

const button = screen.getByRole('button');
fireEvent.click(button);

await waitFor(() => {
? expect(button).not.toBeInTheDocument(); // 啊,好多了
});

但是,如果我們還想測試其他一些項目呢?網(wǎng)上沒有很多關(guān)于如何處理此問題的好的示例,并且在過去的項目中,我已經(jīng)將其他項目放在 waitFor 之外:

// 等待按鈕
await waitFor(() => {
 expect(button).not.toBeInTheDocument();
});

// 然后測試確認消息
const confirmationText = getByText('Logged in as [email?protected]');
expect(confirmationText).toBeInTheDocument();

這有效,但我不喜歡它,因為它使按鈕條件看起來很特殊,即使我們可以輕松地切換這些語句的順序:

// 等待確認消息
await waitFor(() => {
 const confirmationText = getByText('Logged in as [email?protected]');
 expect(confirmationText).toBeInTheDocument();
});

// 然后測試按鈕
expect(button).not.toBeInTheDocument();

在我看來,將與相同更新相關(guān)的所有內(nèi)容一起分組到 waitFor 回調(diào)中要好得多:

await waitFor(() => {
? expect(button).not.toBeInTheDocument();

? const confirmationText = screen.getByText('Logged in as [email?protected]');
? expect(confirmationText).toBeInTheDocument();
});

對于像這樣的簡單斷言,我真的很喜歡這種技術(shù),但在某些情況下,它可能會減慢測試速度,等待在 waitFor 之外立即發(fā)生的失敗。有關(guān)此示例,請參閱 React Testing Library 常用錯誤中的“在單個 waitFor 回調(diào)中有多個斷言”。

對于包含幾個步驟的測試,我們可以連續(xù)使用多個 waitFor 塊:

const button = screen.getByRole('button');
const emailField = screen.getByRole('textbox', { name: 'Email' });

// 填寫表單
fireEvent.change(emailField, { target: { value: '[email?protected]' } });

await waitFor(() => {
? // 檢查按鈕是否已啟用
? expect(button).not.toBeDisabled();
  expect(button).toHaveTextContent('Submit');
});

// 提交表單
fireEvent.click(button);

await waitFor(() => {
? // 檢查按鈕是否不再存在
? expect(button).not.toBeInTheDocument();
});

如果您只等待一個項目出現(xiàn),則可以使用 findBy 查詢代替。它在后臺使用 waitFor。

行內(nèi) it 注釋

另一個測試最佳實踐是編寫更少、更長的測試;這使您可以將測試用例與重要的用戶流程關(guān)聯(lián)起來,同時使測試保持隔離,以避免意外行為。我贊成這種方法,但它在保持代碼組織和記錄所需行為方面可能會帶來挑戰(zhàn)。我們需要未來的開發(fā)人員能夠返回測試并了解它在做什么,為什么它會失敗等等。

例如,假設這些期望之一開始失?。?/p>

it('handles a successful login flow', async () => {
? // 為清晰起見隱藏測試的開頭
?
  expect(button).toBeDisabled();
  expect(button).toHaveTextContent('Loading...');
?
? await waitFor(() => {
? ? expect(button).not.toBeInTheDocument();
? ? expect(emailField).not.toBeInTheDocument();
? ? expect(passwordField).not.toBeInTheDocument();
?
? ? const confirmationText = screen.getByText('Logged in as [email?protected]');
? ? expect(confirmationText).toBeInTheDocument();
? });
});

查看此內(nèi)容的開發(fā)人員無法輕松確定正在測試的內(nèi)容,并且可能難以確定失敗是 錯誤(這意味著我們應該修復代碼)還是 行為更改(這意味著我們應該修復測試)。

我最喜歡的解決方案是使用每個測試的鮮為人知的測試語法,并添加描述正在測試的每個關(guān)鍵行為的行內(nèi) it 樣式注釋:

test('successful login', async () => {
? // 為清晰起見隱藏測試的開頭
?
? // 它設置加載狀態(tài)
? expect(button).toBeDisabled();
  expect(button).toHaveTextContent('Loading...');
?
? await waitFor(() => {
? ? // 它隱藏表單元素
? ? expect(button).not.toBeInTheDocument();
? ? expect(emailField).not.toBeInTheDocument();
? ? expect(passwordField).not.toBeInTheDocument();
?
? ? // 它顯示成功文本和電子郵件地址
? ? const confirmationText = screen.getByText('Logged in as [email?protected]');
? ? expect(confirmationText).toBeInTheDocument();
? });
});

這些注釋不會神奇地與 Jest 集成,因此如果您遇到失敗,失敗的測試名稱將對應于您傳遞給測試標簽的參數(shù),在本例中為“successful login”。但是,Jest 的錯誤消息包含周圍的代碼,因此這些 it 注釋仍然有助于識別失敗的行為。當我從一個期望中刪除 not 時,我收到了以下錯誤消息:

為了獲得更明確的錯誤,有一個名為 jest-expect-message 的包允許您為每個期望定義錯誤消息:

expect(button, 'button is still in document').not.toBeInTheDocument();

一些開發(fā)人員更喜歡這種方法,但我發(fā)現(xiàn)它在大多數(shù)情況下有點 granular 了,因為單個 it 通常涉及多個期望。

團隊的后續(xù)步驟

有時我希望我們可以為人類制定 linter 規(guī)則。如果是這樣,我們可以為我們的團隊設置一個 prefer-integration-tests 規(guī)則,然后就結(jié)束了。

但是,唉,我們需要找到一個更類似的解決方案來鼓勵開發(fā)人員在某些情況下選擇集成測試,例如我們前面介紹的 LoginModule 示例。像大多數(shù)事情一樣,這歸結(jié)于團隊討論您的測試策略,就對項目有意義的內(nèi)容達成一致,并且——希望——在 ADR 中記錄它。

在制定測試計劃時,我們應該避免一種會迫使開發(fā)人員為每個文件編寫測試的文化。開發(fā)人員需要能夠放心地做出明智的測試決策,而不必擔心他們“測試不足”。Jest 的覆蓋率報告可以通過提供一個健全性檢查來幫助解決這個問題,即使測試在集成級別上進行了合并。

我仍然不認為自己是集成測試專家,但是進行這項練習幫助我分解了一個集成測試比單元測試提供更大價值的用例。我希望與您的團隊分享這一點,或者在您的代碼庫上進行類似的練習,將有助于指導您將集成測試納入您的工作流程。

以上是反應集成測試:覆蓋范圍更大,測試較少的詳細內(nèi)容。更多信息請關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

本站聲明
本文內(nèi)容由網(wǎng)友自發(fā)貢獻,版權(quán)歸原作者所有,本站不承擔相應法律責任。如您發(fā)現(xiàn)有涉嫌抄襲侵權(quán)的內(nèi)容,請聯(lián)系admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費脫衣服圖片

Undresser.AI Undress

Undresser.AI Undress

人工智能驅(qū)動的應用程序,用于創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用于從照片中去除衣服的在線人工智能工具。

Clothoff.io

Clothoff.io

AI脫衣機

Video Face Swap

Video Face Swap

使用我們完全免費的人工智能換臉工具輕松在任何視頻中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的代碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

功能強大的PHP集成開發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級代碼編輯軟件(SublimeText3)

什么是'渲染障礙CSS”? 什么是'渲染障礙CSS”? Jun 24, 2025 am 12:42 AM

CSS會阻塞頁面渲染是因為瀏覽器默認將內(nèi)聯(lián)和外部CSS視為關(guān)鍵資源,尤其是使用引入的樣式表、頭部大量內(nèi)聯(lián)CSS以及未優(yōu)化的媒體查詢樣式。1.提取關(guān)鍵CSS并內(nèi)嵌至HTML;2.延遲加載非關(guān)鍵CSS通過JavaScript;3.使用media屬性優(yōu)化加載如打印樣式;4.壓縮合并CSS減少請求。建議使用工具提取關(guān)鍵CSS,結(jié)合rel="preload"異步加載,合理使用media延遲加載,避免過度拆分與復雜腳本控制。

外部與內(nèi)部CSS:最好的方法是什么? 外部與內(nèi)部CSS:最好的方法是什么? Jun 20, 2025 am 12:45 AM

thebestapphachforcssdepprodsontheproject'sspefificneeds.forlargerprojects,externalcsSissBetterDuoSmaintoMaintainability andReusability; forsMallerProjectsorsingle-pageApplications,InternaltCsmightBemoresobleable.InternalCsmightBemorese.it.it'sclucialtobalancepopryseceneceenceprodrenceprodrenceNeed

我的CSS必須在較低的情況下嗎? 我的CSS必須在較低的情況下嗎? Jun 19, 2025 am 12:29 AM

否,CSSDOESNOTHAVETOBEINLOWERCASE.CHOMENDENS,使用flowercaseisrecommondendendending:1)一致性和可讀性,2)避免使用促進性技術(shù),3)潛在的Performent FormanceBenefits,以及4)RightCollaboraboraboraboraboraboraboraboraboraboraboraboraboraboraboraboraborationWithInteams。

CSS案例靈敏度:了解重要的 CSS案例靈敏度:了解重要的 Jun 20, 2025 am 12:09 AM

cssismostlycaseminemintiment,buturlsandfontfamilynamesarecase敏感。1)屬性和valueslikeColor:紅色; prenotcase-sensive.2)urlsmustmustmatchtheserver'server'scase,例如

什么是AutoPrefixer,它如何工作? 什么是AutoPrefixer,它如何工作? Jul 02, 2025 am 01:15 AM

Autoprefixer是一個根據(jù)目標瀏覽器范圍自動為CSS屬性添加廠商前綴的工具。1.它解決了手動維護前綴易出錯的問題;2.通過PostCSS插件形式工作,解析CSS、分析需加前綴的屬性、依配置生成代碼;3.使用步驟包括安裝插件、設置browserslist、在構(gòu)建流程中啟用;4.注意事項有不手動加前綴、保持配置更新、非所有屬性都加前綴、建議配合預處理器使用。

什么是CSS計數(shù)器? 什么是CSS計數(shù)器? Jun 19, 2025 am 12:34 AM

csscounterscanautomationallymentermentermentections和lists.1)usecounter-ensettoInitializize,反插入式發(fā)芽,andcounter()orcounters()

CSS:何時重要(何時不)? CSS:何時重要(何時不)? Jun 19, 2025 am 12:27 AM

在CSS中,選擇器和屬性名不區(qū)分大小寫,而值、命名顏色、URL和自定義屬性則區(qū)分大小寫。1.選擇器和屬性名不區(qū)分大小寫,例如background-color和Background-Color相同。2.值中的十六進制顏色不區(qū)分大小寫,但命名顏色區(qū)分大小寫,如red有效而Red無效。3.URL區(qū)分大小寫,可能導致文件加載問題。4.自定義屬性(變量)區(qū)分大小寫,使用時需注意大小寫一致。

CSS中的情況敏感性:選擇器,屬性和值所解釋的 CSS中的情況敏感性:選擇器,屬性和值所解釋的 Jun 19, 2025 am 12:38 AM

cssselectorsand and propertynamesarecase-insimentimentiment.1)selectorSlike like'div'div'div'div'and'and'and'And'Andiv'areequivalent.2)propertioessuchas'backusuchas'backusuchas'backusuchas'backusuchas'backer'back-and'background and backorgook crolor'backorground-artreateateDthesementhesame.3)

See all articles