使用 Jest 和 React 测试库测试 Next.js 应用程序

在本教程中,我们将完成在 Next.js 应用程序中执行单元测试所需的所有步骤。我们将使用React 测试库Jest来测试我们的应用程序。

Jest 是一个 JavaScript 测试框架,旨在确保任何 JavaScript 代码库的正确性,而不是 React。另一方面,React 测试库构建在 DOM 测试库之上,通过添加 API 来测试 React 组件。Jest 和 React 测试库一起用于 React 和 Next.js 应用程序的单元测试。

入门

我们将首先使用以下命令创建一个支持 Typescript 的新 Next.js 应用程序。

npx create-next-app@latest --ts

为您的项目命名并在您选择的任何代码编辑器中打开它。pages/index.tsx使用下面的代码设置内容。

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来呈现文本“Testing Next.js Applications”。

设置 Jest 和 React 测试库

对于以前版本的 Next.js,我们必须设置 Jest 以支持 Babel,但最新的 Next.js 版本使用内置的 Rust 编译器和 Jest 的内置配置,因此我们不需要任何额外的配置。

要设置 Jest,请安装jest, @testing-library/react,@testing-library/jest-dom作为开发依赖项:

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

安装库后,我们需要设置一个配置文件以使用内置的 Jest 配置。jest.config.js在根目录下创建一个文件并添加以下内容:

// 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接下来,我们在根目录下创建一个文件,并添加以下内容:

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

让我们将 Jest 添加到文件的scripts部分,package.json以在监视模式下运行我们的测试,这将重新运行我们所有的文件更改测试:

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

或者,要为 Jest 启用 ESLint 支持,请将以下内容添加到.eslintrc.json默认的create-next-app.

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

写作测试

按照 Jest 的约定,__tests__在项目的根目录中添加文件夹。我们将在此文件夹中存储所有与测试相关的文件。

在编写测试时,Jest 为您提供了一个describe、 atest和一个it全局函数,用于与 Jest 库进行通信以进行各种测试。该describe函数是一个将相关测试组合在一起的测试套件包装器。该test功能是一个测试,它是套件的一部分并运行单独的单独测试。

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

让我们通过编写一个检查 Jest 设置是否正确的测试来测试我们的测试运行器。在__tests__目录中,创建一个index.test.js文件,添加以下内容:

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 组件

现在 Jest 已经设置好了,我们可以开始为 Next.js 应用程序编写测试了。要开始我们的测试,让我们components在根目录中创建一个文件夹并创建一个新文件components/Heading.tsx. 现在,让我们创建一个 React 组件来呈现h1一个文本,如下所示:

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

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

让我们在文件中导入<Heading />组件,将标签替换为以下代码以呈现 Heading 组件:pages/index.tsx``main

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

检查您的浏览器以查看标题是否正确呈现。现在我们已经pages/index.tsx准备好文件,我们可以开始编写测试了。在该__tests__目录中,创建一个__tests__/components.test.tsx包含以下内容的文件:

// 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 />组件呈现h1带有 text 的标签Testing Next.js Applications
  • 在测试之前,我们使用render函数 from来渲染组件。@testing-library/react
  • 我们还使用该screen.getByRole函数来获取h1我们想要测试的标签。
  • 然后,我们使用expectJest 中的函数来测试h1标签是否在文档中。
  • 最后,我们使用该toBeInTheDocument函数来测试h1标签是否在文档中。

我们现在可以运行我们的测试,如果一切正常,我们的测试将显示一个绿色复选标记以表明一切正常。

测试事件

我们已经测试了一个组件的渲染。是时候测试组件触发的事件了。让我们从创建一个 Button 组件开始。在您的components/Button.tsx文件中并添加以下内容:

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>
...

我们现在可以测试新的按钮组件。在__tests__目录中,在__tests__/components.test.tsx文件中添加以下内容:

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

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

在导入 Button 组件并创建一个默认的按钮 props 对象后,我们可以使用它来测试按钮组件,然后我们添加一个 describe 块来测试按钮组件。在__tests__/components.test.tsx文件中并添加以下内容以测试按钮:

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

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

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

上面的测试将测试按钮组件是否呈现一个按钮。如果按钮在文档中,则测试将通过,否则将失败。我们现在可以运行测试,看看测试是否通过。

随着按钮组件的渲染,我们可以测试按钮的onClick事件。我们正在运行一个简单的测试来检查按钮是否被点击。在__tests__/components.test.tsx文件中,将以下内容添加到describe用于测试按钮组件是否呈现的块中:

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

这里发生了什么?

  • 按钮组件有一个 onClick 属性。测试是检查单击按钮时是否调用了 onClick 属性,它是一个函数。
  • fireEvent.click在这样做的过程中,我们使用React 测试库中的函数模拟单击按钮。提供这种开箱即用的功能是 React 测试库是最佳选择的原因。
  • 在点击模拟之后,我们使用expectJest 中的函数来测试该函数是否被调用,并toHaveBeenCalledTimes检查该函数是否被调用过一次。您可以从JestReact 测试库的文档中了解更多这些功能。

我们可以从这里运行我们的测试,看看测试是否通过,我们可以继续进行下一组测试。

测试 Next.js API 路由

接下来,Next.js API 路由,双关语。Next.js 提供了一种在我们的应用程序中创建 API 路由的方法,在这种情况下,我们可以测试 API 路由。让我们创建一个新的 API,然后我们可以测试它。创建一个新pages/api目录并创建一个新[name].ts文件。

使用Next.js 动态路由,我们希望创建一个 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 文件中,我们正在创建一个处理程序来处理所有传入的 ant 传出请求。然后我们从请求查询中解构name,设置状态码和内容类型,最后返回一个 JSON 对象,其name属性设置为Your name is [name].

有了 API 路由,我们现在为它编写测试。在__tests__目录中,创建一个新__tests__/api.test.ts文件。这是我们将为 API 路由编写测试的地方。注意在不同的文件中进行,我们要在组件文件中测试组件,在 api 文件中测试 API。

我们需要一个库来向我们的 API 路由发出请求。我们可以使用fetch它,因为它已默认添加到 Node.js,但如果您的版本较低,我们可以使用外部库。如果它仍然可用isomorphic-fetch,您仍然可以使用默认浏览器获取。安装并@types/isomorphic-fetch使用以下内容向我们的 API 路由发出请求:

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

__tests__/api.test.ts文件中,让我们导入isomorphic-fetch包。

import fetch from "isomorphic-fetch";

我们现在可以测试我们的 API 路由。在__tests__/api.test.ts文件中,添加以下内容:

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函数向我们的 API 路由发出请求返回正确的数据。我们正在使用expectJest 中的函数来测试请求的状态代码是否为 200,并且我们还在检查我们期望的响应是否确实是我们从请求中得到的。

这是迄今为止测试 Next.js API 路由最简单的方法,我将在另一篇文章中介绍另一种方法。您可以运行测试以查看它是否通过。

结论

我们首先使用 TypeScript 创建了一个新的 Next.js 应用程序,然后设置了 Jest 和 React 测试库以用于我们的测试。我们测试了三个主要方面;在 Next.js 应用程序中测试组件的呈现、测试组件上的事件和测试 API 端点。

谢谢你的时间。下一篇文章见。

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容