写点什么

实战案例|利用 MarsCode 内置的 DeepSeek 服务,单元测试耗时缩短 70%!

  • 2025-03-25
    北京
  • 本文字数:3224 字

    阅读完需:约 11 分钟

资料来源:火山引擎-开发者社区

单元测试总在奇怪的地方卡 bug?Mock 配置像解谜游戏、边界条件比数学题还烧脑?没关系!MarsCode 编程助手 X 三款大模型(DeepSeek V3、DeepSeek R1、豆包大模型 1.5 )帮你解决所有问题无需配置,性能 Top,代码准确率嗖嗖🚀用过的朋友都说: “以前写测试像开手动挡,现在像开了自动巡航” ,速看~👇👇👇


准备工作

在正式开始单元测试之前,我们先做好相关准备👇


  1. 下载/更新 MarsCode 编程助手

1️⃣如果你是新用户,以 Visual Studio Code 中为例,打开 VSCode 扩展窗口,在搜索窗口搜索 MarsCode,找到 MarsCode 插件单击「install」,完成安装,登录即可使用 MarsCode 编程助手。



2️⃣ 如果你是老用户,请更新 MarsCode 编程助手到最新版本(若开启了自动更新,则将会自动更新),更新后重启 IDE 即可

*VSCode:1.1.62

*JetBrains:1.2.1.15


2.克隆案例项目本次案例使用的是 AI 生成的一个最基础的 React 项目,目的是模拟学习工作中最真实的场景,方便大家迅速掌握快速搭建单元测试环境以及生成单元测试用例的技巧。

        git clone https://github.com/ylx911229/unit-test\_back.git
复制代码



3. 单元测试基础*因为后续案例选用的是 Jest 作为单元测试框架,所以介绍的基础内容主要以 Jest 框架为标准


  • 概念介绍

1️⃣ 断言(Assertion) :用于验证测试结果是否符合预期的语句,如 Expect

2️⃣ 测试替身(Test Double) :用于替代真实依赖的模拟对象,确保测试的隔离性,如 Mock、Jest.fn

3️⃣ 测试覆盖率(Test Coverage) :用于衡量测试用例覆盖代码的比例,如 行覆盖率、分支覆盖率

  • 单元测试文件命名规范生成的单元测试文件名必须以 .test.ts/.test.js 作为结尾,否则单元测试框架无法读取并执行单元测试用例

  • 运行单元测试用例

        npx jest --coverage
复制代码

实战跟练

STEP1:搭建测试环境

首先来搭建一下单元测试的环境,向 MarsCode 输入以下提示词:


        Workspace 帮我为整个项目搭建一下单元测试的环境
复制代码



可以看到 MarsCode 给我们推荐的单元测试框架是 Jest,这是一个流行的 JavaScript 测试框架,特别适合用于单元测试。另外,React 组件的单元测试,依赖 React Testing Library ,RTL 是当前 React 生态中最流行的组件测试解决方案,它提供了一套更贴近真实用户行为的测试工具链。


STEP2:单元测试用例编写

关于单元测试用例,将从函数类和 UI 类等不同的方式类型来举例实现


  • 函数类

1️⃣ 纯函数 & 工具类 对于纯函数的工具类单元测试,特点是输入输出明确,无副作用,整体测试重点是 输出格式验证和唯一性检查,接下来以 生成唯一 ID 为例:

// 生成唯一 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,选择生成单测:



同样将 MarsCode 生成的单测代码保存为 validate-utils.test.js 后运行,效果如下:


3️⃣ 状态管理 & 业务逻辑 现在我们来探讨更复杂业务逻辑下的单测,以购物车 Redux reducer 为例,涉及核心业务规则或全局状态变更,这个案例中我们的测试重点在


  • 商品添加逻辑(新增 vs 增量)

  • 促销码有效性验证

  • 不可变数据检查


现在打开 businuss-utils.js,同样选中/test 生成单测:


将 MarsCode 生成的新文件保存为 businuss-utils.test.js,运行单测命令,得到如下效果:


  • UI 类

我们以用户输入花费金额的 REACT 组件为例,我们单测的重点在于事件是否能正确触发以及 UI 是否能正常显示,打开 react-test.jsx 文件:

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 在提升代码质量方面的工程价值。 相信在更复杂的业务场景下,优秀的工具能帮助开发者释放更多生产力。

用户头像

还未添加个人签名 2022-01-25 加入

还未添加个人简介

评论

发布
暂无评论
实战案例|利用MarsCode内置的DeepSeek服务,单元测试耗时缩短70%!_火山引擎开发者社区_InfoQ写作社区