API的多种测试方案
使用vitest 单元测试框架时 ,我们去解决api的测试方案一般会有多种
下面我们划分一下这三种解决方案:
这里列举了多种方案的方式,以及在使用这几种方案分别是代替了哪一层去做,从下图我们可以清晰的看到每种方案所对应的在流程中的作用
1.直接mock axios
首先,我们先看第一种使用mock axios
来处理:
这是我们的case案例,可以在todolist中去增加信息,并且可以通过remove去删除,我们可以看下下面的基本逻辑
内部实现的逻辑部分:内部所有api的调用都是放在pinia里面
updateTodoList 它直接去请求fetchTodoList
,拿到数据之后,我们将其赋值给todos,也就是响应式对象,赋值后视图就自动刷新了。
addTodo 它直接去请求后端接口 fetchAddTodo
,后端接口会返回data和state。请求完后,我们去创建一个todoItem,里面存入后端返回的id和title,判断state后将其传入到todos里面。
removeTodo 它直接去请求fetchRemoveTodo
,基于后端返回到数据,然后根据数据去查找对应的todo,然后再将它删除掉。
其中的api是写好了,对应实际的业务场景十分类似:
接下来就进行 mock axios
回到测试case里面,我们首先去引入axios进行mock,vi.mock("axios")
;然后去使用mocked.(axios.post),因为我们addtodo是一个post。看对应的注释部分代码是用了mockResolvedValue
因为它返回的是一个promise。其中要去写一个data,data中还有一个data,这部分是对应我们上面逻辑去写,对应的mock结构。
这里我们漏测了一个东西,axios的入参也是需要测试的。因为当我们去将入参删掉后,发现并不会影响到我们的测试case,不会去发现问题,直接去返回了对应的数据。
所以我们要通过行为验证,验证axios.post是不是接收到了一个title,所以在其中我们要进行一个验证,也就是代码验证部分去调用axios.post 验证是否被调用。这种方式是行为验证的方式,那么在代码中,其实使用了另一种方式:状态验证去测试,我们先补充vitest api部分。
在模拟 Axios 的 post 方法时:如果你希望模拟返回一个已解决的 Promise 对象,可以使用 mockResolvedValue。如果你需要自定义返回的 Promise 对象的实现逻辑,可以使用 mockImplementation
而去使用 mockImplementation
其中返回的值是传入进来的title,所以如果不去传,那我们就会报错。那么我们也不用去验证最后axios.post,会比较灵活,也方便我们重构。
那么看第二个case就会方便很多,只需要修改一下返回数据,并且需要去准备一组数据,才能进行验证remove。所以第一次的时候我们需要是一个addtodo,第二次我们需要removetodo,这里是使用了 mockImplementationOnce
,和之前的逻辑其实是一样的,这里的id也是需要用户去输入的。
第三个case也是同理,直接去做一个todoList,模拟一个后端返回一个结构。
这样去做完,虽然是可以通过case,但是我们是将外部的axios实现细节进行了暴露。如果有一天它被替代掉,那么耦合在我们测试代码里的axios都需要进行替换掉。而且我们还需要去控制它内部的输入和输出部分,所以这种方案并不是很推荐。
第二种方案 mock 中间层
我们使用这种方式在图中去展示可以发现是去做了中间层api这部分,代替了axios去模拟。
而代码部分的修改,我们之间可以去对比一下:
而其中我们可以看到它暴露的部分比之前是减少了许多,输入只接收一个title,而且返回的时候只用返回data和state,所以这就是我们去mock中间层的方案。其他的两个case可以去改写一下,而且这个方案我们去使用时候,方法我们命名是十分明显的,去做什么比之前要清晰许多。
这种mock中间层是十分去推荐的,虽然去暴露了api的实现细节,但是这些是基本稳定的。
第三种方案 msw
这种方案,mock server worker,这是mock了最后一层,发起ajax请求的时候发起拦截
pnpm i msw -D
先去安装msw
然后创建mocks文件夹,里面创建server.ts
第二步要去在测试case中去借助生命周期钩子
一开始去监听,每个case完成后要去rest,最后的话要去关闭server,这就是初始化msw的一个逻辑。最后再去写内部逻辑是跟koa 和 express是很类似的。下面是进行了一个封装使用的:
使用msw中rest,然后如何去返回值,只需要去res context.json对应的数据结构就可以了。
然后再用server.use去使用mockAddTodo,因为封装好了就方便我们多次调用
这种使用msw去实现是有一定学习成本的,如果对于koa和express熟悉的话,上手很快
初始化逻辑调整
这边最后要处理的一个点就是初始化逻辑,当我们的todo.spec脚本去执行的时候,我们先去执行setup中写好初始化,就可以不用在每一个脚本中去写。
而去写的话我们现在是使用的vite的conifg,vitest其实是可以共用viteconfig,我们可以换成vitest/config,可以解决类型报错。这里的defineConfig 继承vite,所以我们这样替换使用后不会有类型报错的问题。把原先的逻辑封装到setup.ts中配置,就可以使用了。
这样去配置好后,就可以十分清晰我们的代码逻辑。
如果团队想去使用mock server worker的方案,可以去读一下mswjs.io/ 官方文档
以上就是三种api的检测方式介绍