用GPT 从0搭建 Jest 到帮写测试用例

news2024/11/28 18:39:48

本文作者为 360 数据平台部前端开发工程师

为什么要用AI写单测

这个问题分两点来说,第一是为什么要写单测,第二是为什么要用AI。

先来说为什么写单测,这很简单,我们项目或者说我们前端团队有公共模块,包含有组件类型、工具函数类型、hooks类型等,我们最大也是重点的项目,使用的技术栈是 React,因此这里说的 hooks 是指 react hooks,随着前端队伍壮大,开始考虑用开源的思路来维护这些代码,那么单测就是正规化的其中很重要一环。

至于第二点,主要是考虑提效,所以做一次尝试,项目里有祖传的 Jest 配置,但是具体需要什么依赖,需要根据所写的单测是什么类型,以及当前技术栈是什么版本,这就有一定搭建的学习成本,因此,本次设定为从0到1搭建 Jest 环境到跑通 AI 所写的用例。

使用的IDE

Cursor,版本0.2.5:

59c0fbd017dd3aa0d2284ceb48355520.png

AI模型:

不是GPT-4

237978b9e6c1ee736783891b07741e7c.png

想要一直使用中文对话,可以这样配置:

c644aec3785b8dd0efcb3407c4691c09.png

点右上角的图标,打开辅助侧栏,点 MORE,然后告诉他,一直使用中文回答,这个配置目前只能有一条。

例子

Github仓库地址:

https://github.com/bdlite/hooks/tree/main

用来做例子的分支:

preview/previous

感兴趣的话可以一边 clone 下来,用 Cursor 打开文件夹,然后一边跟着操作。

开始

仓库都有什么

93a5f80d023680e19933e8b8b64d9a1d.png
  • 有从我们实际项目里剥离出的工程类配置文件,例如 .babelIrc、.gitignore、jest.config.js,配置项也只留取需要的

  • package.json 是使用 npm init 初始化的,生成过程略,涉及到要添加的包后文也会有提及

  • src 目录就是放源码了,es 目录是编译为 esModules 的制品路径,发布到 npm 用的是这个目录,因此单测也引用的这个目录

  • coverage 目录是 jest 自动生成的报告结果

  • __tests__ 目录是空的

看下祖传的jest.config.js

全选整段文件内容,会出现两个选项(这里的 Cursor 界面还有点小小的 bug),一个是 Edit,一个是 Chat,不同的是,Edit 可以直接帮你生成新代码或者改代码,Chat 是基于这段代码你可以问些问题,但不在编辑框内生成代码。

74595b4d0fb64fdbaaecdcac7d37b003.png

我这里想让它直接帮我生成注释,便于辅助分析一下这个配置有哪些不妥(懒得看官方文档的这里集合

bfc9fe9d6ddfd4264717a84c7daa2aed.png 6f007afcc450c7656a8af3c1da42d453.png

生成的是英文,先 Reject,看来辅助侧栏里面配置的在代码编辑框里并不共用,重新调整 Prompt 输入:

8397d7b2387a4273f3ccd5e9cbbd32dc.png

这一次 Accept:

7db25aaee827d3779d460b8a2d34331d.png

这么读起来,虽然是祖传的配置,似乎看不出什么毛病,当然为了制作例子,其实是有所调整的,至少项目里的路径配的没问题,因此这里的配置也是模拟了一下,路径也没问题,基于此,我们开始让Cursor写用例,然后跑起来看看。

看下源码

ad1f0f4ff6a953859774e46c5fb5e82e.png
image.png

为了保证环境搭建及后续发布流程比较靠谱,我们先 check 一下哪些是 dependencies,哪些是 devDependencies 和 peerDependencies:

  • 'react' 显然不能跟着打包,放 peer

  • 'query-string' 这种库还是要严谨点,放 depend

去 package.json 确认一下是否放对了

a4cfdda187b56f2051a260df76acb0a1.png

这里有个问题,没有指定 react ,因此我让“助理”帮我加一下:

8f548eceb6e34b177d5a027c07450b97.png f5b21de0d7d5b03170107a25886e762f.png

accept 之后多了个花括号,手动删吧,谁让它还是个孩子啊。

好戏开场

9fe75f306f288abdcfe1f5c20e4878ec.png
image.png

我这是当前打开 package.json 的情况下在侧边栏问的,再试试打开 useSearch.js 的情况下问问:

0aa764dc7bc71f749c9fcb0aa0dac7f2.png
image.png

果然当前打开的文件就是提问的上文,可以见到上一个其实并不是我源码实现的内容,我估计是从其他地方找来的答案,果然是一本正经也能胡说八道呀

行,这里的方案给得还是蛮全面的,比方说教你加哪些依赖,手把手教你创建文件,可惜的是,它其实并不能读取整个工程,跨文件去理解上下文,因此它不知道的是,我配了 jest 去读 __tests__ 下的文件

review 下用例

依上文,用例只有三个,只看代码不执行的话,似乎做到了最小的功能检验

再换个思路试试

faa51458c0a6c57bf0c86795cd83f908.png
image.png

从以上两个提问的答案来看,看懂代码是没问题的,也能指出里面有问题的点,表现不错

49d01b2c2b3e22ebc63c4ab4071ef9ee.png
image.png
12d6420bd30157b33e15d04e24849a44.png
image.png

基本上符合源码涉及的几个场景,不过,根据我的判断,似乎还有点问题:

  • 没有开场处的验证 hooks 返回类型的用例

    ef457d3d572b3fcc661e182f25a209e8.png
    image.png
  • 此处的第二个用例:如果 key 不是字符串或者 value 是 null,那么函数不会进行任何操作

    • 看到这个才发现,源码里存在着 bug,为什么在问它有没有 bug 的时候没反应过来,因为当时还没做例子的时候问过,回答是没什么问题,看来这孩子也是变聪明了,也可能是当时的上下文有差异,导致孩子只顾夸,没理性思考,是的,我会去问写得好不好,哈哈

    • bug 就是,其实我们是期望 value 是 null 或者 undefined,那么会清掉 search 中对应的 key

其他问题不大,最多想起来什么场景补充一下就好

run 一下看

按上文提到的,在 __tests__ 目录下创建 useSearch.test.js 文件,然后把刚才的代码复制进去,但是引用 useSearch 函数的路径稍微改一下:

import { useSearch } from 'es/useSearch';

安装 jest 各种依赖

ed73e4c21e5c630542ab95824e67f3a9.png
image.png

根据提示,最省事儿的办法,重新安装,并在命令后加上 --legacy-peer-deps

npm install --save-dev jest @babel/core @babel/preset-env @babel/preset-react babel-jest identity-obj-proxy react-test-renderer --legacy-peer-deps

执行 test 命令

该命令的配置同样是祖传的

45658dd8cbf480118a20e75ab93aed68.png
image.png

在终端里执行:

npm run test

还得装依赖

899de0bb7aff625591ce355a52f09a5f.png
image.png

OK~装它!

731e6a2fc668a063e08f15c10e027dc5.png
image.png

一步一脚印啊,继续装它!

再执行一次,这次是 Module ts-jest in the transform option was not found.

ts-jest 装完了,然后再 run test

8b5ecc5f27918d42b802f431fe42a212.png
image.png

做个例子不容易,那就继续装!

但是这里我会都装到 devDependencies,再 run

41eea8b79929d160176266c879e4a674.png
image.png

打开推荐的链接

6c4f6598130b0a0fe6dc00c5f70d30d2.png
image.png

OK,试一下 use react-test-renderer

86cde73fe3c1f865ec979fb0ae591115.png
image.png

装 react,装到 devDependencies,再 run

e6f8f77da0865c7014d4c263f3f50bfd.png
image.png

执行成功,祖传的命令写得倒没啥问题,上“链接”:

npm install --save-dev cross-env jest-environment-jsdom ts-jest react-dom react-test-renderer react

分析用例没通过的原因

7d5f100598023049e62f2d806e737f27.png
image.png

选中这个用例,问下“助理”

bb80c84d37b2d2d94c29386f31b42574.png
image.png

点 Edit

539567e71f113acbf002720f09a0fdf8.png
image.png
3a434eb9ff2bf68df7f0ce733b7373ef.png
image.png

好吧,怪我给的自由太过火,重新调整下

734643c7ae60a990a41230b9900c61fe.png
image.png
4ddab25c53f16e9fc0177f89b8553ec2.png
image.png

accept 然后跑看看,通过了,不截图了,看下一个

dbd32dc718011ab2e91610d240458262.png
image.png

这个问题原因一致,但其实,这个不符合我们对这个 hooks 的期待,用错误的源码逻辑来生成错误的用例了属于是,直接改

e82f659809c7317c4376d2da2105c98c.png
image.png
fd8cbc0e4475014e880b7201e977e8ae.png
image.png
3a87e001baf8914bd28a48d3e2fdd7ee.png
image.png

why?

看下提示中的 Received 就知道了

  • 第三个用例因为源码确实存在这个 bug,这目前来说是按照“预期” failed了

  • 第四个为什么就不对了,第一次能跑出结果的时候不是通过了么,其实跟第二个用例的问题一样,不严谨导致的,这里面每一个用例中的 window 并不处于块级作用域,像第二个一样改过来就好了

让它改第四个

f3b7bb9f0220de1617ff2cf38ecea004.png
image.png
09bf5b040d482e1e51c3dbd9c389830f.png
image.png

就剩下第三个用例没跑通了

改源码中的 bug

改源码,选中源码,然后 Edit

bc890bed782713a5c10134d4752e09bc.png
image.png
efd075ece8569aa4b8df33af365c7d34.png
image.png

我哭死,感觉它不会写代码,又或者是我的锅?

罢了罢了,先手动改改

import { useCallback } from 'react'
import queryString from 'query-string'

export function useSearch() {
  const searchList = [] // 同一组件连续调用的缓冲区

  const getSearch = useCallback(() => queryString.parse(window.location.search), [ window.location.search ])

  const setSearch = useCallback((key, value = null) => {
    const search = getSearch()

    if (search[key] === `${value}` || typeof key !=='string') return

    searchList.push({ [key]: value })

    const nextSearchData = { ...search, ...searchList.reduce((before, current) => ({ ...before, ...current }), {}) }
    const nextSearch = queryString.stringify(nextSearchData, { skipNull: true })

    window.history.replaceState(queryString.parse(nextSearch), '', `?${nextSearch}`)
  }, [ getSearch, window.history ])

  return { getSearch, setSearch }
}

这里改的是 src 目录下的文件,我们测的时候引用的是 es 的文件,因此在 package.json 的 script 里改下

deec7b974bce01ec84b9c1d76794e4bc.png
image.png

重新执行

d86a58fcd007bbfa90e921e7cae3d949.png
image.png

4个用例终于跑通了,yes!

利用 Prompt 提供上下文修复报错

再把之前提到的类型验证加上

it('should return an object with getSearch and setSearch functions', () => {
    const { getSearch, setSearch } = useSearch();
    expect(typeof getSearch).toBe('function');
    expect(typeof setSearch).toBe('function');
  });

run 后报错:

a930a0b89285431ba71f2819d37d9aa5.png
image.png

用侧边栏的 Chat 求救一下

301a8ca4e4797e81c855d9b333e38f5a.png
image.png

选中用例,Edit

f68e4d4d501464c9242fc96c2d6b4263.png
image.png
476ab6a315228a740c38e7ddd9b62822.png
image.png
a8dfb30600df03dc4c4e145ebeef2d41.png
image.png

喜提大结局,撒花~

总结

本次实验操作路径

  1. 喂源码

  2. 生成用例

  3. 根据提示搭环境

  4. review 用例和源码

  5. 找出问题并修复

  6. 丰富用例

  7. 遇到报错

  8. 喂错误信息

  9. 根据信息修复

  10. 跑通

如果没有喂给比较合适的上下文,可能会得不到准确的答案。

如果给的描述不够精准,例如我让它修复源码的 bug 就不尽如人意,相信给出足够多,足够精准的信息,应该还是可以的,或许你代码的结构上原本就有点问题,AI 不见得能够懂你希望连结构上的问题一起都能优化的心思,限于本文不是生成代码,而是用例,这一块没有展开来做尝试。

工程领域的期望

这个例子搭建 Jest 的过程还是比较顺利的,我在我们的业务项目里搭建,错误信息很难解,例子中的步骤其实是带有一点上帝视角的,包括里面其实已经自带了配置文件。

目前发现 AI 并不能阅读整个工程的配置、某个指定目录下的文件,据我了解,必须把整个工程丢给某个 AI 的程序,才能实现一些特定的任务,这对于追求轻量 IDE 的我们来讲,还远远不够,因此,我还是对于这一点抱有很高的期待,这对再次降低前端门槛将是一个很大的贡献。

描述问题的能力

这虽然不是本文想要提及的主题,大家可以通过例子自己去感受一下。


谢谢你读到了这里~

- END -

关于奇舞团

奇舞团是 360 集团最大的大前端团队,代表集团参与 W3C 和 ECMA 会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。

2fb76b33e06fb4d81034ecc0384c05a4.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/470765.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ipv6地址技术详解

一、什么是IPv6? IPv6是英文“Internet Protocol Version 6”(互联网协议第6版)的缩写,是互联网工程任务组(IETF)设计的用于替代IPv4的下一代IP协议,其地址数量号称可以为全世界的每一粒沙子编…

你可能需要的IDEA-Java开发插件

Idea开发插件 Alibaba Cloud AI Coding Assistant 阿里云智能编码插件(Alibaba Cloud AI Coding Assistant)是一款AI编程助手,它提供代码智能补全和代码示例搜索能力,帮助你更快更高效地写出高质量代码。 让我觉得比较有意思的…

Docker安装在Linux系统上(纯步骤)

Docker安装在Linux系统上操作步骤 本文章只有操作步骤,没有原理解释,只是用来提醒自己安装步骤 下面是docker官网,也有安装详情 https://docs.docker.com/engine/install/centos/ 安装分为四步走 我使用的是CentOS7版本,下面命令…

Linux基本权限

文章目录 前言一、Shell命令以及运行原理1.Shell的定义2.为什么用户不能直接使用kernel? 二、Linux中的权限1.权限是什么?2.如何操作权限呢?(怎么修改权限)1. 修改文件属性2. 修改文件角色 3.为什么要有权限 三、粘滞位…

直接插入排序(Straight Insertion Sort)

本文已收录于专栏 《算法合集》 一、简单释义 1、算法概念 对插入第i个记录时,R1、R2、…、Ri-1均已排好顺序。因此,将第i个记录Ri-1、…、R2、R1进行比较,找到合适的位置插入,他简单明了但是速度很慢。 2、算法目的 把无序数组通…

windows下免费本地部署类ChatGpt的国产ChatGLM-6B

ChatGLM-6B 是一个开源的、支持中英双语的对话语言模型,基于 General Language Model (GLM) 架构,具有 62 亿参数。结合模型量化技术,用户可以在消费级的显卡上进行本地部署(INT4 量化级别下最低只需 6GB 显存)。 Chat…

禁止右键禁止复制禁止选择网页内容的CSS和JS代码(支持PC和移动端)

禁止右键禁止复制禁止选择网页内容的CSS和JS代码(支持PC和移动端),收藏吧,用得上。 body {-moz-user-select: none;/* Firefox私有属性 */-webkit-user-select: none;/* WebKit内核私有属性 */-ms-user-select: none;/* IE私有属…

项目1登录功能优化(中间件添加操作信息,统一日志输出)

中间件添加操作信息 context增加属性userid用于储存访问者id,便于后期使用中间件添加操作信息 type Context struct { //......,context其他属性 Userid int} 对于添加操作信息的中间件函数 // 添加用户操作中间件func Addoperationmid(db *sql.DB) gee7.HandlerFunc { retu…

SpringBoot【运维实用篇】---- 多环境开发

SpringBoot【运维实用篇】---- 多环境开发 1. 多环境开发(yaml单一文件版)2. 多环境开发(yaml多文件版)3. 多环境开发(properties多文件版)4. 多环境开发独立配置文件书写技巧5. 多环境开发控制 讲的内容距…

【LeetCode】1000题挑战(220/1000)

1000题挑战 没有废话,直接开刷! 目录 1000题挑战 没有废话,直接开刷! 第一题:119. 杨辉三角 II - 力扣(Leetcode) 题目接口 解题思路 代码: 过过过过啦!&#x…

【Python】【进阶篇】13、Django安装与配置教程

目录 Django安装与配置教程1. Windows系统安装Django1) 离线安装2) 在线安装3) 配置Django环境变量4) 检查是否安装成功 2. Linux和Mac系统安装Django1) 使用终端在线安装2) 下载安装包离线安装 Django安装与配置教程 本节主要对 Django 在各个平台上的安装方式与配置进行讲解…

Blender 插件UvSquares

目录 1. UvSquares插件1.1 解压UvSquares插件1.2 blender偏好设置1.3 打开插件1.4 安装插件1.5 勾选插件UvSquares1.6 安装UvSquares插件前1.7 安装UvSquares插件后 1. UvSquares插件 Blender 的 UV 编辑器工具,可将 UV 选择重塑为网格。 下载:https:/…

关于jsonp的理解。利用 百度“联想搜索”接口

什么是 回调函数? 在 JavaScript 中,回调函数是指将一个函数作为参数传递给另一个函数,以在某些操作完成后通知调用者。当操作完成时,被调用的函数(即回调函数)将被调用,以执行某些指定的操作或…

Ajax的简单使用

目录 1、ajax概述 2、模拟ajax 3、Jquery实现ajax (1)通用开发步骤 (2)示例 注册用户名重复性验证 (3)示例 ajax解析json数据 (4)实现细节 4、axios实现ajax 5、ajax发送PUT…

西门子安装和配置

一、已安装 V16,检查软件正常 1.判断西门子软件是否正常工作,检查软件图标,如下图。 2.如果软件图标不存在,检查Windows服务是否存在,打开方法如下图: 3.检查西门子的服务,共16个服务&#xff…

万维网服务

~ 在AppSrv上搭建网站服务器 ~ 将访问HTTP://www.chinaskills.com的http的请求重定向到https://chinaskills.com站点 ~ 网站内容设置为“该页面为www.chinaskills.com测试页” ~ 将当前web根目录设置为d:\wwwroot 目录 ~ 启用windows身份验证,只有通过身份验证的…

如何在比特币系统内创造人工生命

信息来源:coingeek.com 自2015年以来,关于比特币能否进行复杂计算以及比特币是否“图灵完备”的争论一直在持续。不幸的是,现在存在着一种流传甚广的谬论,有人说比特币并非图灵完备的,它不能像以太坊区块链那样进行复杂…

【Node.JS 数据库篇】Sequelize 的用法与mysql的关系

文章目录 一、Sequlize是Node.JS中ORM实现二、Sequelize 执行基础的CRUD三、Sequelize 执行复杂的CRUD1. 指定字段attributes2. 触发数据库事务3. LEFT JOIN 联表关系4. INNER JOIN 联表关系5. 嵌套查询 四、Sequlize常用方法和参数1. findAll2. count3. findByPk4. findOne5. …

【策略设计模式详解】C/Java/JS/Go/Python/TS不同语言实现

简介 策略模式(Strategy Pattern)属于行为型设计模式。将每一个算法封装到具有共同接口的独立类中,根据需要来绑定策略,使得具体实现和策略解耦。 当你想使用对象中各种不同的算法变体,使用if...else 所带来的复杂和…

增强型PID-自适应-前馈-神经网络控制研究(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…