在本教程中,我们将完成在 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
我们想要测试的标签。 - 然后,我们使用
expect
Jest 中的函数来测试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 测试库是最佳选择的原因。 - 在点击模拟之后,我们使用
expect
Jest 中的函数来测试该函数是否被调用,并toHaveBeenCalledTimes
检查该函数是否被调用过一次。您可以从Jest和React 测试库的文档中了解更多这些功能。
我们可以从这里运行我们的测试,看看测试是否通过,我们可以继续进行下一组测试。
测试 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 路由发出请求返回正确的数据。我们正在使用expect
Jest 中的函数来测试请求的状态代码是否为 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