前端测试最强教程 - 实现 fake http 和 fake db

因为想要写出完美的测试,我们就需要模拟各种接口状态,各种动态数据,要求再高点甚至需要模拟鉴权,那这就不是简单的覆盖方法可以实现的。

所以我们需要一些第三方依赖,下面介绍一下实现步骤。

实现步骤

step 1: 新增业务并进行 case design

为了让便于展示测试,我给网站加了一个新功能,在首页会展示网站的标题并且支持修改,这样就需要一个 get 接口,一个 post 接口,并且还需要数据库。

于是我用 koa 搭了一个后端服务,提供两个接口,数据库我不熟,所以我另辟蹊径,在 notion 上新建了一个 page,通过 notionSDK 来获取和更新 page name,这样就成了我的数据库。

别看我说的简单,其实这一套花了我一个下午,有各种意想不到的问题,比如 koa 如何解析 body、 notion 数据结构太复杂、eslint。

tsconfig 配置的问题等等,期间还错把notion key 给 push 了上去,已经给 disable 了。

 具体怎么实现就不细讲了,这不是本系列教程的重点,代码都在github.com/alpacachen/… ,感兴趣可以自己去看一下 server 目录。

总之最后实现的需求大致长这个样子。

对这个需求进行 case design,得到:


G: 网站加载完成

T: 会展示标题 “集成测试框架”

W: 点击修改按钮

T: 会弹出修改弹窗

W: 在输入框中输入 “集成测试框架 2”,点击 “确认修改”

T: 弹窗会关闭,网站标题会更新为“集成测试框架 2”

step 2:完善 app-context,支持xhr

install http-request-mock和lokijs 这两个依赖,重写一下 fake-http-handler.ts这个文件

 

import HttpRequestMock from 'http-request-mock';// 引入 mock 请求的依赖

import { HupuPostDb, TitleDb } from './fake-db';// 引入提前准备好的 fakedb

const mocker = HttpRequestMock.setup()

// mock一下 hupu 接口,数据从写死改为从 db 中读

mocker.mock({

url: '/hupu/api/v2/bbs/walkingStreet/threads?page=1',

method: 'GET',

body: { data: { threadList: HupuPostDb.find() } },

});

// mock 获取标题的请求

mocker.mock({

url: '/api/title',

method: 'GET',

response: () => {

return TitleDb.findOne().title

}

});

//mo 修改标题的请求

mocker.mock({

url: '/api/title',

method: 'POST',

response(req: { body: { title: unknown; }; }) {

// 找到并更新

const prevTitle = TitleDb.findOne().title;

TitleDb.findAndUpdate({ title: prevTitle }, (d) => {

d.title = req.body.title

})

return {};

}

});

http-request-mock这个依赖应该是国人开发,有优秀的中文文档,大家可以看一下,我就不赘述了github.com/huturen/htt…

新建文件 fake-db.ts


import loki from 'lokijs'

import { FakeHupuData } from './fake-data'

const db = new loki('fake-db')

// 创建虎扑热帖集合

const HupuPostDb = db.addCollection('hupu-post')

// 插入默认数据

HupuPostDb.insert(FakeHupuData)


//创建 title 集合

const TitleDb = db.addCollection('title')

// 插入默认数据

TitleDb.insert({ title: '集成测试教程' })

export { HupuPostDb, TitleDb }

接下来我们只需要在 app-context.ts中引入一下 fake-http-handler.ts,这样我们的集成测试就支持了接口和数据的库查询修改。

step 3: 根据 case借助 ai 补充新的测试

接下来我们试试在测试跑一遍中调用接口获取数据,修改数据,再次获取最新数据这个流程。

 

describe('W: 点击标题旁边的 修改按钮', () => {

beforeEach(() => {

fireEvent.click(screen.getByTestId('change-title-but'))

})

it('T: 会弹出弹窗,input 中展示 “集成测试教程”', () => {

expect(screen.getByTestId('title-modal')).toBeTruthy()

expect(within(screen.getByTestId('title-modal')).getByDisplayValue('集成测试教程')).toBeTruthy()

})

describe('W: 修改 title 为 “集成测试教程 2” 关闭弹窗', () => {

beforeEach(async () => {

const input = within(screen.getByTestId('title-modal')).getByDisplayValue('集成测试教程')

fireEvent.change(input, { target: { value: '集成测试教程 2' } })

fireEvent.click(screen.getByText('确定修改'))

await vi.advanceTimersToNextTimerAsync();

})

it('T: 弹窗关闭,顶部栏的文案变为“集成测试教程 2”', () => {

// header

expect(screen.queryByTestId('title-modal')).toBeFalsy()

expect(screen.getByTestId('header').innerHTML).toContain('集成测试教程 2')

})

})

})

这里需要强调一下,这个测试我是直接接在了之前的测试后面,很方便而且直观,只就是为什么我在第一章的时候强调写测试规范的问题,如果测试规范,是非常易于维护和新增的,会大大降低写测试的压力,从此形成良性循环

step: 4 见真章

直接看npm run test npm run coverage得到结果

可以看到除了 server 的代码还是一片绿,并且也没有加几行测试代码 🎉

大家不妨想一想,如果没有集成测试,依靠单测想要达到这种程度的覆盖率需要写多少代码,多少文件?至少需要 header.test.ts和 store.test.ts两个文件,还要 mock 数据,想想就觉得麻烦。

所以重要的事情说三遍,在前端领域

绝大多数单元测试没有任何卵用

绝大多数单元测试没有任何卵用

绝大多数单元测试没有任何卵用

请不要再写单元测试让你自己和你的同事难受了,集成测试才是王道。

结尾

我们集成测试框架基本完成,麻雀虽小,却五脏俱全。

能看到这里的同学想必一定会有些收获,可以尝试拿着这一套思路去套用到你们当前的项目中,年终绩效就不用愁。毕竟一个能写好测试的前端还是不多见的。

当然我这些代码还是有很多值得优化的点,有一些 corner case 发现了但是没有补充测试。比如文件目录可以更加合理,app-context的代码可以组织的更好。

但是作为一个入门教程我认为达到这个程度就够了,切勿直接套用到生产项目中,我提供的是思路,不是代码。

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:【文末自行领取】

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值