使用 Jest 和 React 測試庫測試 Next.js 應用程序

在本教程中,我們將完成在 Next.js 應用程序中執(zhí)行單元測試所需的所有步驟。我們將使用React 測試庫Jest來測試我們的應用程序跺撼。

Jest 是一個 JavaScript 測試框架联四,旨在確保任何 JavaScript 代碼庫的正確性,而不是 React鳄梅。另一方面厕怜,React 測試庫構(gòu)建在 DOM 測試庫之上衩匣,通過添加 API 來測試 React 組件。Jest 和 React 測試庫一起用于 React 和 Next.js 應用程序的單元測試粥航。

入門

我們將首先使用以下命令創(chuàng)建一個支持 Typescript 的新 Next.js 應用程序琅捏。

npx create-next-app@latest --ts

為您的項目命名并在您選擇的任何代碼編輯器中打開它。pages/index.tsx使用下面的代碼設置內(nèi)容递雀。

import type { NextPage } from "next";
import Head from "next/head";
import styles from "../styles/Home.module.css";

const Home: NextPage = () => {
  return (
    <div className={styles.container}>
      <Head>
        <title>Testing Next.js</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>Testing Next.js Applications </h1>
      </main>
    </div>
  );
};

export default Home;

pages/index.tsx頁面中柄延,我們正在導入NextPage當前頁面的組件類型和默認next/head組件以設置頁眉。然后缀程,我們使用 Next.js 附帶的默認樣式并設置一個h1來呈現(xiàn)文本“Testing Next.js Applications”拦焚。

設置 Jest 和 React 測試庫

對于以前版本的 Next.js,我們必須設置 Jest 以支持 Babel杠输,但最新的 Next.js 版本使用內(nèi)置的 Rust 編譯器和 Jest 的內(nèi)置配置赎败,因此我們不需要任何額外的配置。

要設置 Jest蠢甲,請安裝jest, @testing-library/react,@testing-library/jest-dom作為開發(fā)依賴項:

npm install --save-dev jest @testing-library/react @testing-library/jest-dom

安裝庫后僵刮,我們需要設置一個配置文件以使用內(nèi)置的 Jest 配置。jest.config.js在根目錄下創(chuàng)建一個文件并添加以下內(nèi)容:

// jest.config.js
const nextJest = require("next/jest");

const createJestConfig = nextJest({
  dir: "./",
});

const customJestConfig = {
  setupFilesAfterEnv: ["<rootDir>/jest.setup.js"],
  moduleDirectories: ["node_modules", "<rootDir>/"],
  testEnvironment: "jest-environment-jsdom",
};

module.exports = createJestConfig(customJestConfig);

查看Next.js 文檔鹦牛,了解默認 Jest 配置中的幕后情況搞糕。

jest.setup.js接下來,我們在根目錄下創(chuàng)建一個文件曼追,并添加以下內(nèi)容:

import "@testing-library/jest-dom/extend-expect";

讓我們將 Jest 添加到文件的scripts部分窍仰,package.json以在監(jiān)視模式下運行我們的測試,這將重新運行我們所有的文件更改測試:

"scripts": {
  "dev": "next dev",
  "build": "next build",
  "start": "next start",
  "lint": "next lint",
  "test": "jest --watch"
}

或者礼殊,要為 Jest 啟用 ESLint 支持驹吮,請將以下內(nèi)容添加到.eslintrc.json默認的create-next-app.

{
  "extends": "next/core-web-vitals",
  "env": {
    "jest": true
  }
}

寫作測試

按照 Jest 的約定,__tests__在項目的根目錄中添加文件夾晶伦。我們將在此文件夾中存儲所有與測試相關(guān)的文件碟狞。

在編寫測試時,Jest 為您提供了一個describe婚陪、 atest和一個it全局函數(shù)族沃,用于與 Jest 庫進行通信以進行各種測試。該describe函數(shù)是一個將相關(guān)測試組合在一起的測試套件包裝器。該test功能是一個測試脆淹,它是套件的一部分并運行單獨的單獨測試常空。

//  `describe` and `test` being used to write a test
describe("my function or component", () => {
  test("does the following", () => {
    ..
  });
});

讓我們通過編寫一個檢查 Jest 設置是否正確的測試來測試我們的測試運行器。在__tests__目錄中盖溺,創(chuàng)建一個index.test.js文件漓糙,添加以下內(nèi)容:

describe("true is true and false is false", () => {
  test("true is true", () => {
    expect(true).toBe(true);
  });

  test("false is false", () => {
    expect(false).toBe(false);
  });
});

有了它,我們可以運行測試npm run test來運行測試咐柜。正確設置 Jest 將在帶有此輸出的測試上顯示一個綠色復選標記:

 PASS  __tests__/index.test.js
  true is true and false is false
    ? true is true (7 ms)
    ? false is false (2 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        1.854 s, estimated 2 s
Ran all test suites.

Watch Usage: Press w to show more.

測試 React 組件

現(xiàn)在 Jest 已經(jīng)設置好了兼蜈,我們可以開始為 Next.js 應用程序編寫測試了攘残。要開始我們的測試拙友,讓我們components在根目錄中創(chuàng)建一個文件夾并創(chuàng)建一個新文件components/Heading.tsx. 現(xiàn)在,讓我們創(chuàng)建一個 React 組件來呈現(xiàn)h1一個文本歼郭,如下所示:

import styles from "../styles/Home.module.css";

export function Heading() {
  return <h1 className={styles.title}>Testing Next.js Applications</h1>;
}

讓我們在文件中導入<Heading />組件遗契,將標簽替換為以下代碼以呈現(xiàn) Heading 組件:pages/index.tsx``main

<main className={styles.main}>
  <Heading />
</main>

檢查您的瀏覽器以查看標題是否正確呈現(xiàn)。現(xiàn)在我們已經(jīng)pages/index.tsx準備好文件病曾,我們可以開始編寫測試了牍蜂。在該__tests__目錄中,創(chuàng)建一個__tests__/components.test.tsx包含以下內(nèi)容的文件:

// components.test.tsx
import { render, screen } from "@testing-library/react";
import Heading from "../components/Heading";

describe("heading component", () => {
  test("renders a heading", () => {
    render(<Heading />);

    const heading = screen.getByRole("heading", {
      name: /testing next\.js applications/i,
    });

    expect(heading).toBeInTheDocument();
  });
});

我們剛剛做了什么泰涂?

  • 我們正在測試的是<Heading />組件呈現(xiàn)h1帶有 text 的標簽Testing Next.js Applications鲫竞。
  • 在測試之前,我們使用render函數(shù) from來渲染組件逼蒙。@testing-library/react
  • 我們還使用該screen.getByRole函數(shù)來獲取h1我們想要測試的標簽从绘。
  • 然后,我們使用expectJest 中的函數(shù)來測試h1標簽是否在文檔中是牢。
  • 最后僵井,我們使用該toBeInTheDocument函數(shù)來測試h1標簽是否在文檔中。

我們現(xiàn)在可以運行我們的測試驳棱,如果一切正常批什,我們的測試將顯示一個綠色復選標記以表明一切正常。

測試事件

我們已經(jīng)測試了一個組件的渲染社搅。是時候測試組件觸發(fā)的事件了驻债。讓我們從創(chuàng)建一個 Button 組件開始。在您的components/Button.tsx文件中并添加以下內(nèi)容:

type ButtonType = { text: string; onClick: () => void };

export default function Button(props: ButtonType) {
  return <button onClick={props.onClick}>{props.text}</button>;
}

然后我們可以導入Button組件并將其添加到pages/index.tsx文件中并將其添加到main標簽中形葬。

...
import Button from "../components/Button";
...

...
<main className={styles.main}>
  <Button text="Click Me" onClick={() => alert("Clicked!")} />
</main>
...

我們現(xiàn)在可以測試新的按鈕組件却汉。在__tests__目錄中,在__tests__/components.test.tsx文件中添加以下內(nèi)容:

import Button from "../components/Button";

const defaultButtonProps = {
  onClick: jest.fn(),
  text: "Submit",
};

在導入 Button 組件并創(chuàng)建一個默認的按鈕 props 對象后荷并,我們可以使用它來測試按鈕組件合砂,然后我們添加一個 describe 塊來測試按鈕組件。在__tests__/components.test.tsx文件中并添加以下內(nèi)容以測試按鈕:

describe("button component", () => {
  it("renders a button", () => {
    render(<Button {...defaultButtonProps} />);

    const button = screen.getByRole("button");

    expect(button).toBeInTheDocument();
  });
});

上面的測試將測試按鈕組件是否呈現(xiàn)一個按鈕。如果按鈕在文檔中翩伪,則測試將通過微猖,否則將失敗。我們現(xiàn)在可以運行測試缘屹,看看測試是否通過凛剥。

隨著按鈕組件的渲染,我們可以測試按鈕的onClick事件轻姿。我們正在運行一個簡單的測試來檢查按鈕是否被點擊犁珠。在__tests__/components.test.tsx文件中,將以下內(nèi)容添加到describe用于測試按鈕組件是否呈現(xiàn)的塊中:

it("calls the onClick function when the button is clicked", () => {
  render(<Button {...defaultButtonProps} />);

  const button = screen.getByRole("button");

  fireEvent.click(button);

  expect(onClick).toHaveBeenCalledTimes(1);
});

這里發(fā)生了什么互亮?

  • 按鈕組件有一個 onClick 屬性犁享。測試是檢查單擊按鈕時是否調(diào)用了 onClick 屬性,它是一個函數(shù)豹休。
  • fireEvent.click在這樣做的過程中炊昆,我們使用React 測試庫中的函數(shù)模擬單擊按鈕。提供這種開箱即用的功能是 React 測試庫是最佳選擇的原因威根。
  • 在點擊模擬之后凤巨,我們使用expectJest 中的函數(shù)來測試該函數(shù)是否被調(diào)用,并toHaveBeenCalledTimes檢查該函數(shù)是否被調(diào)用過一次洛搀。您可以從JestReact 測試庫的文檔中了解更多這些功能敢茁。

我們可以從這里運行我們的測試,看看測試是否通過留美,我們可以繼續(xù)進行下一組測試彰檬。

測試 Next.js API 路由

接下來,Next.js API 路由独榴,雙關(guān)語僧叉。Next.js 提供了一種在我們的應用程序中創(chuàng)建 API 路由的方法,在這種情況下棺榔,我們可以測試 API 路由瓶堕。讓我們創(chuàng)建一個新的 API,然后我們可以測試它症歇。創(chuàng)建一個新pages/api目錄并創(chuàng)建一個新[name].ts文件郎笆。

使用Next.js 動態(tài)路由,我們希望創(chuàng)建一個 API忘晤,該 API 返回一個 JSON 對象宛蚓,其中 aname在 URL 中指定。例如设塔,如果我們訪問/api/john凄吏,我們希望返回一個name屬性設置為的 JSON 對象john

// pages/api/[name].ts
import { NextApiRequest, NextApiResponse } from "next";

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  const {
    query: { name },
  } = req;

  res.statusCode = 200;
  res.setHeader("Content-Type", "application/json");
  res.json({ name: `Your name is ${name}` });
}

我們在這里做什么?

在我們新的 [name].ts 文件中痕钢,我們正在創(chuàng)建一個處理程序來處理所有傳入的 ant 傳出請求图柏。然后我們從請求查詢中解構(gòu)name,設置狀態(tài)碼和內(nèi)容類型任连,最后返回一個 JSON 對象蚤吹,其name屬性設置為Your name is [name].

有了 API 路由,我們現(xiàn)在為它編寫測試随抠。在__tests__目錄中裁着,創(chuàng)建一個新__tests__/api.test.ts文件。這是我們將為 API 路由編寫測試的地方拱她。注意在不同的文件中進行二驰,我們要在組件文件中測試組件,在 api 文件中測試 API椭懊。

我們需要一個庫來向我們的 API 路由發(fā)出請求诸蚕。我們可以使用fetch它步势,因為它已默認添加到 Node.js氧猬,但如果您的版本較低,我們可以使用外部庫坏瘩。如果它仍然可用isomorphic-fetch盅抚,您仍然可以使用默認瀏覽器獲取。安裝并@types/isomorphic-fetch使用以下內(nèi)容向我們的 API 路由發(fā)出請求:

npm install --save isomorphic-fetch @types/isomorphic-fetch

__tests__/api.test.ts文件中倔矾,讓我們導入isomorphic-fetch包妄均。

import fetch from "isomorphic-fetch";

我們現(xiàn)在可以測試我們的 API 路由。在__tests__/api.test.ts文件中哪自,添加以下內(nèi)容:

describe("api routes", () => {
  it("should return the correct data", async () => {
    const data = await fetch("http://localhost:3000/api/Duncan");
    expect(data.status).toBe(200);

    const json = await data.json();
    expect(json).toEqual({ name: "Your name is Duncan" });
  });
});

我們剛剛做了什么丰包?

  • 在上面的代碼中,我們正在測試 API 路由是否使用fetch函數(shù)向我們的 API 路由發(fā)出請求返回正確的數(shù)據(jù)壤巷。我們正在使用expectJest 中的函數(shù)來測試請求的狀態(tài)代碼是否為 200邑彪,并且我們還在檢查我們期望的響應是否確實是我們從請求中得到的。

這是迄今為止測試 Next.js API 路由最簡單的方法胧华,我將在另一篇文章中介紹另一種方法寄症。您可以運行測試以查看它是否通過。

結(jié)論

我們首先使用 TypeScript 創(chuàng)建了一個新的 Next.js 應用程序矩动,然后設置了 Jest 和 React 測試庫以用于我們的測試有巧。我們測試了三個主要方面;在 Next.js 應用程序中測試組件的呈現(xiàn)悲没、測試組件上的事件和測試 API 端點篮迎。

謝謝你的時間。下一篇文章見。

文章來源:https://blog.astrosaurus.me/testing-nextjs-applications-with-jest-and-react-testing-library

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末甜橱,一起剝皮案震驚了整個濱河市享言,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌渗鬼,老刑警劉巖览露,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異譬胎,居然都是意外死亡差牛,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門堰乔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來偏化,“玉大人,你說我怎么就攤上這事镐侯≌焯郑” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵苟翻,是天一觀的道長韵卤。 經(jīng)常有香客問我,道長崇猫,這世上最難降的妖魔是什么沈条? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮诅炉,結(jié)果婚禮上蜡歹,老公的妹妹穿的比我還像新娘。我一直安慰自己涕烧,他們只是感情好月而,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著议纯,像睡著了一般父款。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上痹扇,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天铛漓,我揣著相機與錄音,去河邊找鬼鲫构。 笑死浓恶,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的结笨。 我是一名探鬼主播包晰,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼湿镀,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了伐憾?” 一聲冷哼從身側(cè)響起勉痴,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎树肃,沒想到半個月后蒸矛,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡胸嘴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年雏掠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片劣像。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡乡话,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出耳奕,到底是詐尸還是另有隱情绑青,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布屋群,位于F島的核電站闸婴,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏谓晌。R本人自食惡果不足惜掠拳,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一癞揉、第九天 我趴在偏房一處隱蔽的房頂上張望纸肉。 院中可真熱鬧,春花似錦喊熟、人聲如沸柏肪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽烦味。三九已至,卻和暖如春壁拉,著一層夾襖步出監(jiān)牢的瞬間谬俄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工弃理, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留溃论,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓痘昌,卻偏偏與公主長得像钥勋,于是被迫代替她去往敵國和親炬转。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容