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

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

反應(yīng)集成測試:覆蓋范圍更大,測試較少

Apr 07, 2025 am 09:20 AM

React Integration Testing: Greater Coverage, Fewer Tests

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

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

測試分為三種類型:

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

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

開始進(jìn)行 React 測試

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

您可以成功登錄:

…或者遇到來自 API 的錯(cuò)誤消息:

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

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

選項(xiàng) 1:單元測試

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

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

這看起來像這樣(為了清晰起見,我在測試文件名中添加了 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 上完成了添加所有這些單元測試的練習(xí),并創(chuàng)建了一個(gè) test:coverage:unit 腳本以生成覆蓋率報(bào)告(Jest 的內(nèi)置功能)。我們可以通過四個(gè)單元測試文件實(shí)現(xiàn) 100% 的覆蓋率:

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

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

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

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

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

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

? await waitForNextUpdate();

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

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

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

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

選項(xiàng) 2:集成測試

讓我們考慮添加一個(gè)集成測試來驗(yàn)證此流程的替代方法:

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

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

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

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);

  // 它設(shè)置加載狀態(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();
  });
});

我真的很喜歡這個(gè)測試,因?yàn)樗鼜挠脩舻慕嵌闰?yàn)證了整個(gè)登錄流程:表單、加載狀態(tài)和成功確認(rèn)消息。集成測試非常適合 React 應(yīng)用程序,正是因?yàn)檫@種用例;用戶體驗(yàn)是我們想要測試的 內(nèi)容,而這幾乎總是涉及 多個(gè)不同的代碼片段協(xié)同工作。

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

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

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

與其考慮單元測試與集成測試,不如讓我們退一步,考慮一下我們?nèi)绾螞Q定首先需要測試什么。需要測試 LoginModule,因?yàn)樗且粋€(gè)我們希望使用者(應(yīng)用程序中的其他文件)能夠放心地使用的實(shí)體。

另一方面,不需要測試 onLogin hook,因?yàn)樗皇?LoginModule 的實(shí)現(xiàn)細(xì)節(jié)。但是,如果我們的需求發(fā)生變化,并且 onLogin 在其他地方有用例,那么我們將需要添加我們自己的(單元)測試來驗(yàn)證其作為可重用實(shí)用程序的功能。(我們也需要移動(dòng)該文件,因?yàn)樗辉偬囟ㄓ?LoginModule 了。)

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

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

其他好處

既然我們在這里,我想談?wù)勔恍椭业募蓽y試保持清晰和有序的語法技巧。

清晰的 waitFor 塊

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

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

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

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

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

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

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

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

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

這有效,但我不喜歡它,因?yàn)樗拱粹o條件看起來很特殊,即使我們可以輕松地切換這些語句的順序:

// 等待確認(rèn)消息
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ù),但在某些情況下,它可能會(huì)減慢測試速度,等待在 waitFor 之外立即發(fā)生的失敗。有關(guān)此示例,請參閱 React Testing Library 常用錯(cuò)誤中的“在單個(gè) waitFor 回調(diào)中有多個(gè)斷言”。

對于包含幾個(gè)步驟的測試,我們可以連續(xù)使用多個(gè) 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();
});

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

行內(nèi) it 注釋

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

例如,假設(shè)這些期望之一開始失?。?/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)容,并且可能難以確定失敗是 錯(cuò)誤(這意味著我們應(yīng)該修復(fù)代碼)還是 行為更改(這意味著我們應(yīng)該修復(fù)測試)。

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

test('successful login', async () => {
? // 為清晰起見隱藏測試的開頭
?
? // 它設(shè)置加載狀態(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();
? });
});

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

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

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

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

團(tuán)隊(duì)的后續(xù)步驟

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

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

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

我仍然不認(rèn)為自己是集成測試專家,但是進(jìn)行這項(xiàng)練習(xí)幫助我分解了一個(gè)集成測試比單元測試提供更大價(jià)值的用例。我希望與您的團(tuán)隊(duì)分享這一點(diǎn),或者在您的代碼庫上進(jìn)行類似的練習(xí),將有助于指導(dǎo)您將集成測試納入您的工作流程。

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

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

熱AI工具

Undress AI Tool

Undress AI Tool

免費(fèi)脫衣服圖片

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Clothoff.io

Clothoff.io

AI脫衣機(jī)

Video Face Swap

Video Face Swap

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

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費(fèi)的代碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

功能強(qiáng)大的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會(huì)阻塞頁面渲染是因?yàn)闉g覽器默認(rèn)將內(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延遲加載,避免過度拆分與復(fù)雜腳本控制。

外部與內(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)避免使用促進(jìn)性技術(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是一個(gè)根據(jù)目標(biāo)瀏覽器范圍自動(dòng)為CSS屬性添加廠商前綴的工具。1.它解決了手動(dòng)維護(hù)前綴易出錯(cuò)的問題;2.通過PostCSS插件形式工作,解析CSS、分析需加前綴的屬性、依配置生成代碼;3.使用步驟包括安裝插件、設(shè)置browserslist、在構(gòu)建流程中啟用;4.注意事項(xiàng)有不手動(dòng)加前綴、保持配置更新、非所有屬性都加前綴、建議配合預(yù)處理器使用。

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

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

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

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

什么是圓錐級函數(shù)? 什么是圓錐級函數(shù)? Jul 01, 2025 am 01:16 AM

theconic-Gradient()functionIncsscreatesCircularGradientsThatRotateColorStopSaroundAcentralPoint.1.IsidealForPieCharts,ProgressIndicators,colordichers,colorwheels和decorativeBackgrounds.2.itworksbysbysbysbydefindefingincolordefingincolorstopsatspecificains off.

See all articles