|
本文作者为360数据平台部前端开发工程师为什么要用AI写单测这个问题分两点来说,第一是为什么要写单测,第二是为什么要用AI。先来说为什么写单测,这很简单,我们项目或者说我们前端团队有公共模块,包含有组件类型、工具函数类型、hooks类型等,我们最大也是重点的项目,使用的技术栈是 React,因此这里说的 hooks 是指 react hooks,随着前端队伍壮大,开始考虑用开源的思路来维护这些代码,那么单测就是正规化的其中很重要一环。至于第二点,主要是考虑提效,所以做一次尝试,项目里有祖传的 Jest 配置,但是具体需要什么依赖,需要根据所写的单测是什么类型,以及当前技术栈是什么版本,这就有一定搭建的学习成本,因此,本次设定为从0到1搭建 Jest 环境到跑通 AI 所写的用例。使用的IDECursor,版本0.2.5:AI模型:不是GPT-4想要一直使用中文对话,可以这样配置:点右上角的图标,打开辅助侧栏,点 MORE,然后告诉他,一直使用中文回答,这个配置目前只能有一条。例子Github仓库地址:https://github.com/bdlite/hooks/tree/main用来做例子的分支:preview/previous感兴趣的话可以一边 clone 下来,用 Cursor 打开文件夹,然后一边跟着操作。开始仓库都有什么有从我们实际项目里剥离出的工程类配置文件,例如 .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 是基于这段代码你可以问些问题,但不在编辑框内生成代码。我这里想让它直接帮我生成注释,便于辅助分析一下这个配置有哪些不妥(懒得看官方文档的这里集合生成的是英文,先 Reject,看来辅助侧栏里面配置的在代码编辑框里并不共用,重新调整 Prompt 输入:这一次 Accept:这么读起来,虽然是祖传的配置,似乎看不出什么毛病,当然为了制作例子,其实是有所调整的,至少项目里的路径配的没问题,因此这里的配置也是模拟了一下,路径也没问题,基于此,我们开始让Cursor写用例,然后跑起来看看。看下源码image.png为了保证环境搭建及后续发布流程比较靠谱,我们先 check 一下哪些是 dependencies,哪些是 devDependencies 和 peerDependencies:'react' 显然不能跟着打包,放 peer'query-string' 这种库还是要严谨点,放 depend去 package.json 确认一下是否放对了这里有个问题,没有指定 react ,因此我让“助理”帮我加一下:accept 之后多了个花括号,手动删吧,谁让它还是个孩子啊。好戏开场image.png我这是当前打开 package.json 的情况下在侧边栏问的,再试试打开 useSearch.js 的情况下问问:image.png果然当前打开的文件就是提问的上文,可以见到上一个其实并不是我源码实现的内容,我估计是从其他地方找来的答案,果然是一本正经也能胡说八道呀行,这里的方案给得还是蛮全面的,比方说教你加哪些依赖,手把手教你创建文件,可惜的是,它其实并不能读取整个工程,跨文件去理解上下文,因此它不知道的是,我配了 jest 去读 __tests__ 下的文件review 下用例依上文,用例只有三个,只看代码不执行的话,似乎做到了最小的功能检验再换个思路试试image.png从以上两个提问的答案来看,看懂代码是没问题的,也能指出里面有问题的点,表现不错image.pngimage.png基本上符合源码涉及的几个场景,不过,根据我的判断,似乎还有点问题:没有开场处的验证 hooks 返回类型的用例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 各种依赖image.png根据提示,最省事儿的办法,重新安装,并在命令后加上 --legacy-peer-depsnpm install --save-dev jest @babel/core @babel/preset-env @babel/preset-react babel-jest identity-obj-proxy react-test-renderer --legacy-peer-deps执行 test 命令该命令的配置同样是祖传的image.png在终端里执行:npm run test还得装依赖image.pngOK~装它!image.png一步一脚印啊,继续装它!再执行一次,这次是 Module ts-jest in the transform option was not found.ts-jest 装完了,然后再 run testimage.png做个例子不容易,那就继续装!但是这里我会都装到 devDependencies,再 runimage.png打开推荐的链接image.pngOK,试一下 use react-test-rendererimage.png装 react,装到 devDependencies,再 runimage.png执行成功,祖传的命令写得倒没啥问题,上“链接”:npm install --save-dev cross-env jest-environment-jsdom ts-jest react-dom react-test-renderer react分析用例没通过的原因image.png选中这个用例,问下“助理”image.png点 Editimage.pngimage.png好吧,怪我给的自由太过火,重新调整下image.pngimage.pngaccept 然后跑看看,通过了,不截图了,看下一个image.png这个问题原因一致,但其实,这个不符合我们对这个 hooks 的期待,用错误的源码逻辑来生成错误的用例了属于是,直接改image.pngimage.pngimage.pngwhy看下提示中的 Received 就知道了第三个用例因为源码确实存在这个 bug,这目前来说是按照“预期” failed了第四个为什么就不对了,第一次能跑出结果的时候不是通过了么,其实跟第二个用例的问题一样,不严谨导致的,这里面每一个用例中的 window 并不处于块级作用域,像第二个一样改过来就好了让它改第四个image.pngimage.png就剩下第三个用例没跑通了改源码中的 bug改源码,选中源码,然后 Editimage.pngimage.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 里改下image.png重新执行image.png4个用例终于跑通了,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 后报错:image.png用侧边栏的 Chat 求救一下image.png选中用例,Editimage.pngimage.pngimage.png喜提大结局,撒花~总结本次实验操作路径喂源码生成用例根据提示搭环境review 用例和源码找出问题并修复丰富用例遇到报错喂错误信息根据信息修复跑通如果没有喂给比较合适的上下文,可能会得不到准确的答案。如果给的描述不够精准,例如我让它修复源码的 bug 就不尽如人意,相信给出足够多,足够精准的信息,应该还是可以的,或许你代码的结构上原本就有点问题,AI 不见得能够懂你希望连结构上的问题一起都能优化的心思,限于本文不是生成代码,而是用例,这一块没有展开来做尝试。工程领域的期望这个例子搭建 Jest 的过程还是比较顺利的,我在我们的业务项目里搭建,错误信息很难解,例子中的步骤其实是带有一点上帝视角的,包括里面其实已经自带了配置文件。目前发现 AI 并不能阅读整个工程的配置、某个指定目录下的文件,据我了解,必须把整个工程丢给某个 AI 的程序,才能实现一些特定的任务,这对于追求轻量 IDE 的我们来讲,还远远不够,因此,我还是对于这一点抱有很高的期待,这对再次降低前端门槛将是一个很大的贡献。描述问题的能力这虽然不是本文想要提及的主题,大家可以通过例子自己去感受一下。谢谢你读到了这里~-END-关于奇舞团奇舞团是 360 集团最大的大前端团队,代表集团参与 W3C 和 ECMA 会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。
|
|