单元测试
是前端开发的关键环节。它能确保代码按预期运行,并在上线前预防错误。无论你是刚入门,还是希望系统提升测试能力,本指南都将帮助你从单测的入门
走向进阶
。
什么是单元测试?
简单来说,单元测试就是把代码中的每个小功能单独拿出来测试,看看是否按预期工作。就像检查汽车的每个零件,确保各部分都正常运转。
为什么要进行单元测试?
- ✅ 尽早发现错误 – 在开发阶段就能发现潜在问题
- ✅ 提高代码质量 – 强制我们写出更好的代码结构
- ✅ 使重构更安全 – 重构时有测试保障,不怕破坏现有功能
- ✅ 记录预期行为 – 测试本身就是最好的文档
新手入门:写第一个测试
第一步:选择测试工具
流行的前端测试框架:
- Jest(推荐用于 React、Vue 和 JavaScript)
- Mocha + Chai(灵活但需要更多设置)
- Vitest(快速,兼容 Vite)
第二步:安装 Jest
npm install --save-dev jest
在 package.json
中添加测试脚本:
{
"scripts": {
"test": "NODE_OPTIONS=--experimental-vm-modules jest"
}
}
新建 jest.config.js
文件:
export default {
testEnvironment: "node",
roots: ["<rootDir>/src"],
moduleFileExtensions: ["js", "jsx", "json"],
transform: {},
};
第三步:写你的第一个测试
让我们测试一个简单的加法函数:
sum.js
function sum(a, b) {
return a + b;
}
export { sum };
sum.test.js
import { sum } from "./sum";
test("将两个数字相加", () => {
expect(sum(1, 2)).toBe(3);
});
运行测试:
npm test
恭喜,你已经完成了第一个单元测试!其实并不难。
进阶阶段:测试 React 组件
第一步:安装 React 测试工具
npm install --save-dev @testing-library/react @testing-library/jest-dom
新建 test/setupTests.js
文件,全局引入 @testing-library/jest-dom
提供的匹配器:
require("@testing-library/jest-dom");
第二步:测试 React 组件
Button.js
import React from "react";
function Button({ onClick, children }) {
return <button onClick={onClick}>{children}</button>;
}
export default Button;
Button.test.js
import React from "react";
import { jest } from "@jest/globals";
import { render, fireEvent } from "@testing-library/react";
import Button from "./Button";
test("按钮被点击时会调用 onClick", () => {
const handleClick = jest.fn();
const { getByText } = render(<Button onClick={handleClick}>Click Me</Button>);
fireEvent.click(getByText("Click Me"));
expect(handleClick).toHaveBeenCalledTimes(1);
});
test("按钮正确渲染", () => {
const { getByText } = render(<Button>Submit</Button>);
expect(getByText("Submit")).toBeInTheDocument();
});
高级技巧:模拟和异步测试
模拟 API 调用
fetchData.js
async function fetchData(url) {
const response = await fetch(url);
return response.json();
}
export { fetchData };
fetchData.test.js
import { fetchData } from "./fetchData";
// 模拟 fetch 函数
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ data: "Mocked data" }),
})
);
test("fetchData 返回模拟数据", async () => {
const data = await fetchData("https://api.example.com");
expect(data).toEqual({ data: "Mocked data" });
expect(fetch).toHaveBeenCalledWith("https://api.example.com");
});
测试自定义钩子
useCounter.js
import { useState } from "react";
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount(count + 1);
return { count, increment };
}
export default useCounter;
useCounter.test.js
import React from "react";
import { render, fireEvent } from "@testing-library/react";
import useCounter from "./useCounter";
function CounterTest({ initial = 0 }) {
const { count, increment } = useCounter(initial);
return (
<div>
<span data-testid="count">{count}</span>
<button onClick={increment}>inc</button>
</div>
);
}
test("useCounter 在点击后自增", () => {
const { getByTestId, getByText } = render(<CounterTest initial={0} />);
expect(getByTestId("count").textContent).toBe("0");
fireEvent.click(getByText("inc"));
expect(getByTestId("count").textContent).toBe("1");
});

写好单元测试的秘诀
- ✔ 测试功能,不测试实现 – 关注“做什么”,而不是“怎么做”
- ✔ 一个测试只做一件事 – 每个测试只验证一个功能点
- ✔ 起个好名字 – 测试名称让人一看就懂在测什么
- ✔ 隔离外部依赖 – 让测试不依赖网络、数据库等外部环境
- ✔ 自动化运行 – 在代码提交时自动运行测试
写在最后
单元测试就像给代码买保险。虽然前期需要投入时间,但能让你在后续开发中更加安心。建议从简单函数开始练习,逐步扩展到组件与异步逻辑。记住,好的测试不仅能帮你发现 bug,还能让代码质量更上一层楼!
原文链接:https://code.ifrontend.net/archives/1392,转载请注明出处。
评论0