前端测试最强教程 (3/3)- 实现 fake http 和 fake db

前言

经过本系列教程的 1 、 2 两部分,我们已经可以跑起来一个集成测试了,但是有个致命的问题,就是接口的返回是通过 mock window.fetch 来实现的,相当于数据写死,这很不灵活,因为想要写出完美的测试,我们就需要模拟各种接口状态,各种动态数据,要求再高点甚至需要模拟鉴权,那这就不是简单的覆盖方法可以实现的。所以我们需要一些第三方依赖,下面介绍一下实现步骤。

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,得到

xml 复制代码
G: 网站加载完成
T: 会展示标题 "集成测试框架"
    W: 点击修改按钮
    T: 会弹出修改弹窗
        W: 在输入框中输入 "集成测试框架 2",点击 "确认修改"
        T: 弹窗会关闭,网站标题会更新为"集成测试框架 2"

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

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

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

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 补充新的测试

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

ts 复制代码
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的代码可以组织的更好。但是作为一个入门教程我认为达到这个程度就够了,切勿直接套用到生产项目中,我提供的是思路,不是代码。

如果觉得我这三篇文章帮助到你们了,就帮忙点个赞吧!

相关推荐
Dnelic-7 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记
世间万物皆对象14 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
Dnelic-1 天前
解决 Android 单元测试 No tests found for given includes:
android·junit·单元测试·问题记录·自学笔记
岳哥i1 天前
前端项目接入单元测试手册
前端·单元测试
qq_433716952 天前
Selenium+Pytest自动化测试框架 ------ 禅道实战
自动化测试·软件测试·selenium·单元测试·pytest·接口测试·压力测试
Dreams°1232 天前
【大数据测试ETL:从0-1实战详细教程】
大数据·数据仓库·python·单元测试·etl
敲代码敲到头发茂密2 天前
怎么做好白盒测试?
java·数据库·mysql·算法·单元测试·模块测试·测试覆盖率
安冬的码畜日常4 天前
【玩转 Postman 接口测试与开发2_007】第六章:Postman 测试脚本的创建(下):预请求脚本及环境变量在多个请求自动运行中的应用
测试工具·postman·测试·runner·api测试·自动测试
Dreams°1235 天前
【大数据测试HDFS + Flask详细教程与实例】
大数据·功能测试·hdfs·单元测试·flask
CSXB996 天前
三十八、Python(pytest框架-上)
python·功能测试·测试工具·单元测试·pytest