使用 Mocha + chai 测试 React 应用

最后更新于

<hr />

最近在继续开发之前的 Kindle UI 库,想到该写一下测试,于是决定使用 Mocha 的测试方案(尝试一些新东西)。

(此段由GPT生成👉)在开发过程中,保证代码质量和正确性是非常重要的。测试是达到这个目的的有效方法之一,特别是在 JavaScript 开发中。在 React 应用开发中,Mocha 和 Chai 是两个非常流行的测试框架。本文将介绍如何使用 Mocha 和 Chai 测试 React 应用。

为什么不用 react 官方推荐的 Jest 测试?

jest 最初是为测试 react app 设计的。相比 Jest, Mocha 具有更高的灵活性(后者可以且必须进行额外的配置,而前者开箱即用),可以在浏览器和 node 环境下运行,并且支持一些复杂的语句例如:

expect(person.age).to.be.lte(35).and.to.be.gte(18);Q

由于我的项目并非单个 react app 而是 monorepo,所以在 workspace 根目录使用 mocha 无疑是更好的选择。

安装依赖

Mocha 本身是不支持 JSX 的,所以我们要安装一些依赖:

yarn add mocha -D

# Babel 配套插件
yarn add -W -D @babel/preset-env @babel/preset-react @babel/preset-typescript @babel/register

# 此依赖用于渲染 react, 请根据 react 版本选择依赖版本!
yarn add react-test-renderer@17.0.2 -D -W

注意:如果你在 workspace 根目录安装测试依赖(推荐的做法),记得添加 -W 参数。

配置 Mocha

在项目根目录创建一个 .mocharc.js 文件,内容如下:

module.exports = {
	extension: ["js", "mjs", "ts", "tsx"],
	ignore: ["**/build/**", "**/node_modules/**"],
	recursive: true,
	timeout: (process.env.CIRCLECI === "true" ? 5 : 2) * 1000, // Circle CI has low-performance CPUs.
	reporter: "dot",
	require: [require.resolve("./test/utils/setupBabel")],
	"watch-ignore": [
		".git",
		"**/node_modules/**",
		"**/build/**",
		"docs/.next/**",
	],
};

在 /test/utils/setupBabel 目录创建一个 setupBabel.js 文件

require("@babel/register")({
	extensions: [".js", ".ts", ".tsx"],
});

配置 Babel

有了 babel, 我们可以使用 cjs 格式的测试文件而无需进行 "type": "module" 这种令人讨厌的设置。

// babel.config.js
module.exports = function getBabelConfig(api) {
	const useESModules = api.env(["legacy", "modern", "stable", "rollup"]);

	const presets = [
		[
			"@babel/preset-env",
			{
				browserslistEnv: process.env.BABEL_ENV || process.env.NODE_ENV,
				modules: useESModules ? false : "commonjs",
			},
		],
		[
			"@babel/preset-react",
			{
				runtime: "automatic",
			},
		],
		"@babel/preset-typescript",
	];

	const plugins = [];

	return {
		assumptions: {
			noDocumentAll: true,
		},
		presets,
		plugins,
		ignore: [/@babel[\\\\|/]runtime/], // Fix a Windows issue.
		overrides: [
			{
				exclude: /\\.test\\.(js|ts|tsx)$/,
				plugins: ["@babel/plugin-transform-react-constant-elements"],
			},
		],
	};
};

测试

在 package.json 添加快捷脚本,此处请根据实际项目配置:

"test:unit": "cross-env NODE_ENV=test mocha --config .mocharc.js 'packages/kindle-ui/**/*.test.{mjs,js,ts,tsx}' 'test/utils/**/*.test.{js,ts,tsx}'"

之后我们可以愉快地编写测试了。

关于 react-test-renderer 的更多用法,可以参考官方文档。

此处列举一些常见测试场景供参考:

import * as React from "react";
import { expect } from "chai";
import renderer from "react-test-renderer";
// 建议使用 link 后的包,以确保贴近实际场景。
import { ListItem } from "@kindle-ui/core";

describe("<ListItem />", () => {
	describe("prop: component", () => {
		it("renders a div", () => {
			const component = renderer.create(<ListItem />);
			expect(component.toTree().rendered).to.have.property("type", "div");
		});

		it("renders a link", () => {
			const component = renderer.create(
				<ListItem component="a" href="#" />
			);
			expect(component.toTree().rendered).to.have.property("type", "a");
		});
	});
});

it("render in Container", () => {
	expect(() =>
		renderer.create(
			<Container>
				<ListItem>test</ListItem>
			</Container>
		)
	).not.to.throw();
});

改进

react 官方提供的测试库功能比较局限,例如,我们无法测试一个元素是否可见,也无法模拟用户操作页面。

所以,我们可以使用@testing-library/react来进行进一步改进。

注意,node 环境下没有 document 对象,需要使用 JSDom 这个库模拟一个。

先为 mocha 添加钩子:

// .mocharc.js

module.exports = {
    {/**... */}
	beforeEach: () => {
		const dom = new JSDOM("", {
			pretendToBeVisual: true,
			url: "<http://localhost>",
		});
		global.window = dom.window;
	},
};

更新测试代码:

import { queries, within } from "@testing-library/react/pure";

describe("general rendering", async () => {
	it("render in Container", async () => {
		render(<ListItem>KindleUI</ListItem>);

		expect(
			within(document.body, { ...queries })
				.getByRole("ListItem")
				.toHaveTextContent("KindleUI")
		).to.be(true);
	});
});