
资料来源:火山引擎-开发者社区      
单元测试总在奇怪的地方卡 bug?Mock 配置像解谜游戏、边界条件比数学题还烧脑?
  没关系!
  MarsCode 编程助手 X 三款大模型(DeepSeek V3、DeepSeek R1、豆包大模型1.5 )帮你解决所有问题
  无需配置,性能Top,代码准确率嗖嗖🚀
  用过的朋友都说:  “以前写测试像开手动挡,现在像开了自动巡航” ,速看~👇👇👇
  准备工作
  在正式开始单元测试之前,我们先做好相关准备👇
     -  
               下载/更新MarsCode 编程助手
    
  1️⃣如果你是新用户,以Visual Studio Code中为例,打开VSCode 扩展窗口,在搜索窗口搜索MarsCode,找到MarsCode 插件单击「install」,完成安装,登录即可使用MarsCode 编程助手。
  
  2️⃣ 如果你是老用户,请更新MarsCode 编程助手到最新版本(若开启了自动更新,则将会自动更新),更新后重启IDE即可
  *VSCode:1.1.62
  *JetBrains:1.2.1.15
  
     -  
               克隆案例项目
    
  本次案例使用的是AI生成的一个最基础的React项目,目的是模拟学习工作中最真实的场景,方便大家迅速掌握快速搭建单元测试环境以及生成单元测试用例的技巧。
              |   Bash                                       git clone https://github.com/ylx911229/unit-test\_back.git                       |     
     
  
     -  
               单元测试基础
    
  *因为后续案例选用的是Jest作为单元测试框架,所以介绍的基础内容主要以Jest框架为标准
  •                     概念介绍
  1️⃣  断言(Assertion) :用于验证测试结果是否符合预期的语句,如  Expect
  2️⃣  测试替身(Test Double) :用于替代真实依赖的模拟对象,确保测试的隔离性,如 Mock、Jest.fn
  3️⃣  测试覆盖率(Test Coverage) :用于衡量测试用例覆盖代码的比例,如  行覆盖率、分支覆盖率
  •                     单元测试文件命名规范
  生成的单元测试文件名必须以  .test.ts/.test.js 作为结尾,否则单元测试框架无法读取并执行单元测试用例
  •                     运行单元测试用例
              |   Markdown                                       npx jest --coverage                       |     
     
  实战跟练
  STEP1:搭建测试环境
  首先来搭建一下单元测试的环境,向MarsCode输入以下提示词:
              |   Markdown                                       Workspace 帮我为整个项目搭建一下单元测试的环境                       |     
     
  
  可以看到MarsCode给我们推荐的单元测试框架是Jest,这是一个流行的JavaScript测试框架,特别适合用于单元测试。
  另外,React组件的单元测试,依赖 React Testing Library ,RTL是当前 React 生态中最流行的组件测试解决方案,它提供了一套更贴近真实用户行为的测试工具链。
  STEP2:单元测试用例编写
  关于单元测试用例,将从函数类和UI类等不同的方式类型来举例实现
  •                     函数类
  1️⃣ 纯函数 & 工具类 对于纯函数的工具类单元测试,特点是输入输出明确,无副作用,整体测试重点是 输出格式验证和唯一性检查,接下来以  生成唯一 ID 为例:
              |   JavaScript  // 生成唯一 IDfunction generateId(prefix) {             return `${prefix}_${Math.random().toString(36).slice(2, 9)}`;               }            |     
     
  我们可以打开src\utils\tool-utils.js,选中代码,在对话框直接选择/test功能形成单元测试:
  
  将生成的单测代码另存为tool-utils.test.js,保存后执行得到如下效果:
  
  结果显示覆盖率100%,但是有一个用例并未通过,显示特殊字符作为前缀输出的结果未匹配正则。
  
  我们可以切换到DeepSeek R1模型并选中文件,将出现的问题告诉MarsCode后将生成的代码替换进原来的tool-utils.test.js,再次运行 npx jest --coverage 可以发现问题已解决~
  
  
  如果test代码未运行成功出现报错,可以将报错内容复制给MarsCode,利用AI问答继续解决问题。
  2️⃣ 数据转换 & 验证
  接下来以用户资料表单处理器为例,处理结构化数据或验证规则,这则测试案例重点在于正常数据清洗(trim、类型转换)、异常输入(空值、非法邮箱、年龄不足)、 及错误消息准确性,打开validate-utils.js,选择生成单测:
              |   JavaScript  // 转换并验证用户输入  export const processUserInput = (formData) => {   const result = {             name: formData.name.trim(),             age: parseInt(formData.age, 10),             email: formData.email.toLowerCase()   };                          if (isNaN(result.age)) {             throw new Error('Invalid age: must be a number');   }                          if (!/^[\w.+]+@\w+\.\w+$/.test(result.email)) throw new Error('Invalid email');             if (result.age < 18) throw new Error('Underage');                             return resu<               }            |     
     
  
  同样将MarsCode生成的单测代码保存为validate-utils.test.js后运行,效果如下:
  
  3️⃣ 状态管理 & 业务逻辑 现在我们来探讨更复杂业务逻辑下的单测,以购物车 Redux reducer为例,涉及核心业务规则或全局状态变更,这个案例中我们的测试重点在
  •                     商品添加逻辑(新增 vs 增量)
  •                     促销码有效性验证
  •                     不可变数据检查
              |   JavaScript  // cartReducer 函数  export const cartReducer = (state = { items: [] }, action) => {             switch (action.type) {             case 'ADD_ITEM':             const existing = state.items.find(item => item.id === action.payload.id);             if (existing) {             return {           ...state,             items: state.items.map(item =>             item.id === action.payload.id               ? { ...item, qty: item.qty + 1 }               : item           )         };       }             return { ...state, items: [...state.items, action.payload] };                                 case 'APPLY_PROMO':             if (!action.payload.isValid) return state;             return { ...state, promoCode: action.payload.code };                                 default:             return state;   }               }            |     
     
  现在打开businuss-utils.js,同样选中/test 生成单测:
  
  将MarsCode 生成的新文件保存为businuss-utils.test.js,运行单测命令,得到如下效果:
  
  •                     UI类
  我们以用户输入花费金额的REACT组件为例,我们单测的重点在于事件是否能正确触发以及UI是否能正常显示,打开react-test.jsx文件:
              |   JavaScript  import React from 'react';               import { render, fireEvent } from '@testing-library/react';               import { CostInput } from './react-test';                            describe('CostInput Component', () => {             beforeEach(() => {     jest.clearAllMocks();                          // 设置全局变量             global.navigator = {             // 传入navigator的值     };                          global.document = {             // 传入document的值     };                          global.window = {             // 传入window的值     };                          global.otherVariables = {             // 传入otherVariables的值     };   });                          test('renders without crashing', () => {             render(<CostInput />);   });                          test('calls handleChange when the input changes with valid number', () => {             const handleChange = jest.fn();             const { getByLabelText } = render(<CostInput handleChange={handleChange} />);     fireEvent.change(getByLabelText('cost-input'), { target: { value: '123' } });             expect(handleChange).toHaveBeenCalled();   });                          test('does not call handleChange when the input is invalid', () => {             const handleChange = jest.fn();             const { getByLabelText } = render(<CostInput handleChange={handleChange} />);     fireEvent.change(getByLabelText('cost-input'), { target: { value: 'abc' } });             expect(handleChange).toHaveBeenCalled();   });                          test('displays the correct value with dollar sign', () => {             const { getByLabelText } = render(<CostInput />);     fireEvent.change(getByLabelText('cost-input'), { target: { value: '123' } });             expect(getByLabelText('cost-input')).toHaveValue('$123');   });                          test('displays empty value when input is invalid', () => {             const { getByLabelText } = render(<CostInput />);     fireEvent.change(getByLabelText('cost-input'), { target: { value: 'abc' } });             expect(getByLabelText('cost-input')).toHaveValue('');   });                          test('handles initial dollar sign correctly', () => {             const { getByLabelText } = render(<CostInput />);     fireEvent.change(getByLabelText('cost-input'), { target: { value: '$123' } });             expect(getByLabelText('cost-input')).toHaveValue('$123');   });                          test('handles empty input correctly', () => {             const { getByLabelText } = render(<CostInput />);     fireEvent.change(getByLabelText('cost-input'), { target: { value: '' } });             expect(getByLabelText('cost-input')).toHaveValue('');   });                          test('handles input with leading zeros correctly', () => {             const { getByLabelText } = render(<CostInput />);     fireEvent.change(getByLabelText('cost-input'), { target: { value: '00123' } });             expect(getByLabelText('cost-input')).toHaveValue('$123');   });                          test('handles input with multiple dollar signs correctly', () => {             const { getByLabelText } = render(<CostInput />);     fireEvent.change(getByLabelText('cost-input'), { target: { value: '$$123' } });             expect(getByLabelText('cost-input')).toHaveValue('$123');   });               });            |     
     
  
  向MarsCode 输入单测/test,同样将生成的代码另存为react_tes t.test.js进行运行,运行之后发现单测覆盖率只有62.5%,不太理想。
  
  我们可以继续让MarsCode 生成单测补充用例,提高覆盖率
  
  应 用代码后,可以看到单测覆盖率 提升到83.33%,同时帮我们优化了部分代码,甚至对原来的handleChange函数进行了优化,提升了代码质量。
  
  通过本次实践,我们不仅掌握了单元测试的核心价值与实施方法,更验证了 MarsCode 在提升代码质量方面的工程价值。 相信在更复杂的业务场景下,优秀的工具能帮助开发者释放更多生产力。
还没有评论,来说两句吧...