您现在的位置是:网站首页 > 不写单元测试(“测试是 QA 的事”)文章详情

不写单元测试(“测试是 QA 的事”)

单元测试是开发过程中不可或缺的一环,但总有人认为“测试是 QA 的事”,这种观点不仅错误,还会导致代码质量下降、维护成本增加。以下从多个角度分析为什么前端开发必须重视单元测试。

为什么前端需要单元测试?

前端代码的复杂性越来越高,从简单的 DOM 操作到复杂的单页应用(SPA),再到状态管理、异步逻辑等,如果没有单元测试,很难保证代码的可靠性。例如:

// 一个简单的工具函数,用于格式化日期
function formatDate(date, format) {
  if (!date) return '';
  const d = new Date(date);
  return format.replace('YYYY', d.getFullYear())
               .replace('MM', String(d.getMonth() + 1).padStart(2, '0'))
               .replace('DD', String(d.getDate()).padStart(2, '0'));
}

如果没有单元测试,以下问题可能被忽略:

  • 输入 nullundefined 时是否会崩溃?
  • 月份和日期是否正确地补零?
  • 传入非法日期字符串时是否会抛出错误?

不写单元测试的后果

1. 代码质量无法保证

前端代码运行在用户端,一旦出错直接影响用户体验。例如:

// 一个计算购物车总价的函数
function calculateTotal(cartItems) {
  return cartItems.reduce((total, item) => total + item.price * item.quantity, 0);
}

如果 cartItems 中包含 pricenull 的项,或者 quantity 为字符串,计算结果可能是 NaN,但如果没有测试,这个问题可能直到线上报错才会被发现。

2. 重构时缺乏信心

没有单元测试时,重构代码就像走钢丝。例如:

// 旧代码:直接操作 DOM
function updateCounter(value) {
  document.getElementById('counter').textContent = value;
}

// 新代码:改用 React
function Counter({ value }) {
  return <div id="counter">{value}</div>;
}

如果没有测试验证新旧逻辑是否一致,可能会遗漏边界情况(如 value0null 时的表现)。

3. QA 成本增加

依赖 QA 手动测试前端逻辑效率极低。例如:

  • 一个表单验证函数有 10 种校验规则,每次改动都需要 QA 重新测试所有场景。
  • 一个动态加载数据的组件,需要模拟网络延迟、错误响应等场景。

如何开始写单元测试?

1. 选择测试框架

前端常用的测试工具:

  • Jest:功能全面,支持快照测试、Mock 等。
  • Vitest:基于 Vite,速度快。
  • Cypress:端到端测试。

2. 测试工具函数

从最简单的工具函数开始。例如测试 formatDate

import { formatDate } from './dateUtils';

test('formatDate handles null input', () => {
  expect(formatDate(null, 'YYYY-MM-DD')).toBe('');
});

test('formatDate pads month and day', () => {
  expect(formatDate('2023-1-1', 'YYYY-MM-DD')).toBe('2023-01-01');
});

3. 测试 React/Vue 组件

使用 @testing-library/react 测试组件:

import { render, screen } from '@testing-library/react';
import Counter from './Counter';

test('Counter displays zero correctly', () => {
  render(<Counter value={0} />);
  expect(screen.getByText('0')).toBeInTheDocument();
});

4. 测试异步逻辑

模拟 API 请求:

import { fetchUser } from './api';
import { mockFetch } from './testUtils';

test('fetchUser handles network error', async () => {
  mockFetch(null, { status: 500 });
  await expect(fetchUser(123)).rejects.toThrow('Network error');
});

常见反对意见的反驳

“写测试太浪费时间”

  • 短期:写测试确实需要额外时间。
  • 长期:减少调试和修复 Bug 的时间,尤其是复杂项目。

“QA 会覆盖所有场景”

  • QA 更擅长整体流程测试,而非细节逻辑。
  • 单元测试能覆盖 QA 难以触达的边界条件(如内存泄漏、竞态条件)。

“我的代码很简单,不需要测试”

  • 简单代码今天可能变成复杂代码明天。
  • 即使是“简单”代码也可能隐藏 Bug(如 parseInt('08') 返回 0)。

测试覆盖率不是银弹

高覆盖率不等于高质量测试。例如:

// 糟糕的测试:只验证了函数被调用,没验证结果
test('updateUser calls API', () => {
  const mockApi = jest.fn();
  updateUser(mockApi, { name: 'Alice' });
  expect(mockApi).toHaveBeenCalled();
});

好的测试应该:

  • 验证输入输出。
  • 覆盖边界条件(空值、非法输入等)。
  • 避免过度 Mock。

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步