用GPT 从0搭建 Jest 及帮写测试用例 | 前端开发

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

为什么要用AI写单测

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

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

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

使用的IDE

Cursor,版本0.2.5:

d6d8cdbcfb269448ebf131196111efed.png

AI模型:

不是GPT-4

215da8bd7678f2247c308f322edddc1b.png

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

37d4b64a82f7c1ac1cb3d5e8deb4e685.png

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

例子

Github仓库地址:

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

用来做例子的分支:

preview/previous

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

开始

仓库都有什么

4abaf11d777b5b65cab839eab8105c6a.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 是基于这段代码你可以问些问题,但不在编辑框内生成代码。

f95ec972992de006abd8c1c8a14a15ea.png

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

25cc487312100127fa9a37465f12e070.png 345ede2fdc9cdf0be12ae66d74100a24.png

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

a932ccd318eec41dd59426f3e13ec04a.png

这一次 Accept:

3d22fe04bd642cf7084c81ed3e00e33f.png

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

看下源码

d5ae5b9a9e2009274bf6dad7d8d1e9e1.png
image.png

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

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

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

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

2ebf6a80d286de78403faa7ae88f28fb.png

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

4c21a2781f5b760b8316816eddcf982e.png b8fcf23820be5c89493de2eb3b188622.png

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

好戏开场

2376ad03266d9500a0ec1df62433dca6.png
image.png

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

ec15ed854cdc0a4d594d2e870404509d.png
image.png

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

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

review 下用例

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

再换个思路试试

e675dcce34c69fb43b8672ef17f7bd72.png
image.png

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

1c7ebe4035bdb3ec1eb646cab3b104c6.png
image.png
b9118a8fbc531fa20d97444a7594a428.png
image.png

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

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

    545adbf716a7a8d0a86b5b52aa4eb94d.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 各种依赖

30a20cb44e040154abfe03758d9b4c6b.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 命令

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

cd125cd121ee93aaaba0b84f7a82cd75.png
image.png

在终端里执行:

npm run test
还得装依赖
f91ddde1efb0ea6c06899614a78ae446.png
image.png

OK~装它!

bc1ca8e95014afddc282b4d0f22b8c7b.png
image.png

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

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

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

526dad3fef2993f54a6d78482767e4e5.png
image.png

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

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

5aa1bd3bd32d4c23100e847949a0e6e5.png
image.png

打开推荐的链接

c4a412605322265115b42bb85db1de7b.png
image.png

OK,试一下 use react-test-renderer

6c80ca3daeb7d3f49971456074f81750.png
image.png

装 react,装到 devDependencies,再 run

76afbe66a7706aa5be21a646e086f6ae.png
image.png

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

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

分析用例没通过的原因

727904c98a74fcb6bd4283ffebb0ee1c.png
image.png

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

ee6838cdd80f86d3c13ff8dd94c01a20.png
image.png

点 Edit

45c4fcd7bf081f3505f20a4ef4c6f2c8.png
image.png
2f42b4a01a3f950aa059ea0b605a26a7.png
image.png

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

50dd79ed62108560939d6ba1b9351c77.png
image.png
68f84d6ef27c096e18c0a86de39b145d.png
image.png

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

a8522ae1eef7959e425ab4fa74bd33c8.png
image.png

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

8a1db97372f1abc074fdb26bbcb51d04.png
image.png
71f52a5823abeaf838d8e0ac89986865.png
image.png
4168799d64d6665e3720ba519eebaeea.png
image.png

why?

看下提示中的 Received 就知道了

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

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

让它改第四个

49d58b21b80b927e1c48bd8146c76f60.png
image.png
ff821cae5b1a7613c2643eb1db477b94.png
image.png

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

改源码中的 bug

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

3167a3817fa82ed72f51883682fddd83.png
image.png
205e92d487af909a053dd094a42ee016.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 里改下

ed0b46a9e763dfdd837f70f541d6320b.png
image.png

重新执行

a0cadc2f546349b87814271b974a91d6.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 后报错:

9be4ba090cc925ce905e0f6114d07fb8.png
image.png

用侧边栏的 Chat 求救一下

7879a05de944e0c99a0cdc678b6db339.png
image.png

选中用例,Edit

81818f502d608d1b768210b721406bb8.png
image.png
a6d90779689a7bc47900fd2f22473d57.png
image.png
7303499feae4e78eaa85f0041520419c.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 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。

803188d8d2aa38a977b60c3cd0053abf.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值