Skip to content

三方登录与组件测试(三) ​

本页对应 三方登录与组件测试,共 6 段视频,约 1 小时 31 分。

学习目标 ​

  • 能够使用 Mock.js 规则为接口数据随机生成 ID、中文标题、中文内容、图片、时间及指定范围数字,并保证字段与接口文档一致。
  • 能够通过浏览器请求本地 Mock 接口,验证返回 10 条随机数据且字段类型、取值范围符合预期。
  • 能够安装并配置 Vitest,编写针对工具函数的测试用例,使用 test、expect、toBe 断言输入与预期输出是否一致。
  • 能够运行测试命令并根据通过/失败结果定位逻辑错误,说明单元测试与 TypeScript 静态类型检查在预防 Bug 上的区别。
  • 能够使用组件测试工具挂载 Vue 组件,断言初始渲染内容,并验证 props 变化能否驱动组件视图更新。
  • 能够模拟用户点击等交互,断言组件是否触发自定义事件、事件参数是否正确,并验证事件值回传后组件状态与视图是否同步更新。

知识点 ​

先让 mock 接口的 data 有内容 ​

为什么需要:上一节已经把 mock 接口的基本结构搭起来了,但 response.data 还是空的。页面请求虽然能走 mock,却拿不到消息列表,业务开发仍然无法继续。
怎么想:模拟接口要尽量像真实后台,返回的数据字段必须和接口文档一致;先模拟 10 条,够列表开发调试即可。
怎么做:回到 mock/index.ts,把原来空的 data 改成数组,用 for 循环往数组里 push 10 条数据。每条数据先按接口文档准备字段:id、title、content、avatar、createTime、status、type。
易错点:字段名、字段类型、枚举范围都要和接口文档对齐;status 只能是 0 或 1,type 只能是 1、2、3;不要只改了 data 却忘了在原 mock 接口的 response 中返回它。

先用写死数据跑通链路 ​

为什么需要:在引入随机规则前,先确认“请求能到 mock、data 能返回、页面能打印”这条链路是通的,否则后面生成随机数据时很难判断问题出在哪。
怎么想:先不追求真实,只写固定值,例如 title: '这是标题'、content: '这是内容'、avatar: 'http://'、status: 0、type: 1,createTime 先给一个时间值。
怎么做:在 for 循环里 data.push({ ... }),长度写 10。保存后刷新浏览器,打开日志,应该能看到 10 条数据。转写稿里的调试过程就是先刷新页面看日志,确认已经出现 10 条,但发现“都是写死的数据,看起来不真实”。
易错点:avatar 要给完整链接形式;createTime 要保持时间类型或接口约定格式;如果刷新后还是线上真实接口数据,要检查请求地址是否用了本地开发环境域名——mock 只在本地开发服务器里生效。

用 mockjs 生成随机真实数据 ​

为什么需要:写死数据只能证明链路通,不能覆盖列表展示、图片展示、状态判断等场景。用 mockjs 生成随机数据,才能更接近真实后台返回。
怎么想:到 mockjs 官方文档找生成规则,先用 @ctitle 试标题,确认能生成随机中文标题,再按同样方式改其他字段。转写稿中先只改 title,刷新后看到标题变成随机中文,证明规则可用,然后继续生成 id、content、avatar、createTime、status、type。
怎么做:在 push 时使用 Mock.mock,可以传规则字符串。按转写稿整理的核心代码如:

ts
import Mock from 'mockjs'

const data: any[] = []

for (let i = 0; i < 10; i++) {
  data.push({
    id: Mock.mock('@id'),
    title: Mock.mock('@ctitle(5,10)'),
    content: Mock.mock('@ctitle(10,20)'),
    avatar: Mock.mock("@image('100x100')"),
    createTime: Mock.mock('@datetime'),
    status: Mock.mock('@integer(0,1)'),
    type: Mock.mock('@integer(1,3)')
  })
}

// 再把 data 放到原 mock 接口的 response.data 中返回

规则含义:@id 生成随机唯一标识;@ctitle(5,10) 生成 5 到 10 个中文字符的标题;content 用 @ctitle(10,20) 生成更长中文内容;@image('100x100') 生成 100×100 图片地址;@datetime 生成随机时间;@integer(0,1) 生成 0 或 1;@integer(1,3) 生成 1 到 3。
易错点:是 @ctitle 不是 @c title;图片尺寸写法是 @image('100x100');@integer 范围要按字段枚举写;Mock.mock 可以传字符串,也可以传对象规则,不要和 Random 方法混着写错。

刷新验证与切换到线上接口 ​

为什么需要:生成完不能只看代码,要确认页面日志里的每条数据都符合接口文档。
怎么做:刷新浏览器,展开日志逐条检查:id 是否随机、title 和 content 是否中文、avatar 是否图片地址、createTime 是否时间、status 是否只在 0/1、type 是否只在 1/2/3。转写稿中还复制了 avatar 地址到浏览器访问,确认能看到一张 100×100 的随机图片。
易错点:如果某个字段仍然写死,检查是否只改了 title 忘了改其他字段;如果图片打不开,检查 @image 规则和尺寸写法。将来真实接口开发好后,在调用接口的地方去掉本地开发环境域名,就会正常访问线上接口;mock 相关配置和生成逻辑不要在没有确认前随意删除。

为什么需要生成更真实的 mock 数据 ​

上一节已经把模拟接口打通了,但 data 还是空数组。为什么需要继续生成数据?因为页面开发不能只验证“接口能通”,还要验证列表渲染、长标题、长内容、头像、时间、状态和类型等不同情况。怎么写死几条数据也能看效果,但十条一模一样的数据不真实,无法支撑业务调试。所以思路是:模拟接口返回的字段必须和接口文档保持一致,先生成 10 条,再用 Mock.js 的规则把字段变成随机但受约束的数据。易错点是字段名、字段类型和取值范围不能自己乱改,否则页面按真实接口开发后会对不上。

先写死 10 条数据,确认链路 ​

为什么先写死?因为要先确认“循环生成 10 条 → response 返回 → 页面能拿到”这条链路没问题。怎么想?在 mock/index.ts 里准备一个 data 数组,用 for 循环往里面 push 10 次,每个对象包含接口文档里的字段:id、title、content、avatar、createTime、status、type。怎么做?可以先写假数据,例如标题写“这是标题”、内容写“这是内容”、头像写一个 http:// 开头的链接、status 写 0、type 写 1。刷新浏览器后,在控制台日志里展开就能看到 10 条数据。易错点是:循环长度要写 10;push 的对象字段要和接口文档一致;status 只能是 0 或 1,type 只能是 1、2、3,不能先写超出范围的值。

用 Mock.js 生成随机字段 ​

为什么不用写死数据?因为写死的数据看起来不真实,也无法覆盖不同状态。怎么想?到 Mock.js 官方文档里找生成规则,先搜索 @ctitle 验证中文标题,再逐个替换字段。怎么做?在 push 时调用 Mock.mock(),传入规则字符串。标题用 @ctitle(5,10),表示 5 到 10 个中文字;内容用 @ctitle(10,20),字数比标题多一些;id 用 @id;avatar 用 @image(100x100) 生成 100×100 图片;createTime 用 @datetime;status 用 @integer(0,1);type 用 @integer(1,3)。可以先只改 title,刷新看到随机中文标题后再补其他字段,这样调试更稳。易错点是:规则要写成字符串,例如 Mock.mock('@ctitle(5,10)');@image 的宽高用小写 x;@integer 不要拼成 interger;字段名 avatar、createTime、status、type 不要写错。

ts
import Mock from 'mockjs'

const data = []

for (let i = 0; i < 10; i++) {
  data.push({
    id: Mock.mock('@id'),
    title: Mock.mock('@ctitle(5,10)'),
    content: Mock.mock('@ctitle(10,20)'),
    avatar: Mock.mock('@image(100x100)'),
    createTime: Mock.mock('@datetime'),
    status: Mock.mock('@integer(0,1)'),
    type: Mock.mock('@integer(1,3)')
  })
}

export default [
  {
    // url、method、timeout 沿用上一节已经配置好的模拟接口
    response: {
      // code、message 等基础结构沿用上一节
      data
    }
  }
]

刷新验证与图片访问 ​

为什么还要刷新验证?因为随机规则是否生效,只看代码看不出来。怎么想?刷新页面后看控制台日志:id 应是随机标识,title 和 content 应是随机中文,avatar 应是图片地址,createTime 应是时间,status 只能落在 0/1,type 只能落在 1/2/3。怎么做?多展开几条数据检查范围,再复制 avatar 地址到浏览器访问,能看到一张 100×100 的随机图片。易错点是:如果请求还是走线上真实接口,通常是因为模拟接口只在本地开发环境生效,请求时要使用本地开发域名才会走 mock;将来接口开发好后,在调用接口处去掉本地开发域名,就会正常访问线上接口。

易错点与切换线上接口 ​

为什么最后强调切换?因为 mock 只是开发阶段的支撑,不能带到线上。怎么想?模拟接口的字段和后台保持一致,页面业务就可以先按这份数据开发;接口完成后只改请求域名。怎么做?调用接口处保留路径,去掉本地开发域名即可。易错点是:不要把写死数据当成最终数据;不要忘记 vite-plugin-mock 和 Mock.js 的配置;不要把 mock/index.ts 里的接口地址、请求方式、超时和 response 基础结构改乱;生成规则不确定时回官方文档查,不要凭感觉写。这样 10 条随机消息数据就能稳定支撑消息列表开发。

为什么已经有 TypeScript 还要写单元测试 ​

很多同学会问:前面引入 TypeScript 时不是说它能“预防无意引入的 bug”吗,为什么还要再引入单元测试?

这两者的检查层次不一样。TypeScript 做的是静态类型检查,它只保证你传进去的类型对不对,不会判断你的逻辑执行得是否正确。单元测试做的是逻辑正确性验证:给函数一个输入,看它能不能产出预期的输出。逻辑写错了,类型再对也拦不住,而单元测试能拦住这一类 bug。

所以思路是:类型检查管“形状”,单元测试管“行为”,二者互补,不是重复。

什么时候引入测试最合适 ​

结论是:越早越好。

原因在于复杂度会随时间增长。一个函数越封装越复杂,依赖越来越多;等到项目后期再补测试,你要考虑的点非常多,写起来会非常麻烦。所以在项目初期就引入,成本最低。

那为什么本课把测试放到最后讲?因为实际开发中一般项目很少引入测试,这里放在最后只是做扩展,让你知道测试该怎么写、能带来什么收益,而不是要求你每个项目都必须上测试。

测试的三种类型与本节范围 ​

测试通常分三类:单元测试、组件测试、端到端测试。本节只讲单元测试。

单元测试的对象是函数、类或组合式函数。判断标准很简单:给函数一个输入,看它能不能达到你预想的输出;达到了,就代表这个函数通过测试。

要完成“输入 → 预期输出”的判断,需要一个测试框架。这里选 Vitest(V 取 Vite 之意),它是基于 Vite 的测试框架,用法和大家熟悉的 Jest 很像,只是它构建在 Vite 之上。而我们项目本来就用 Vite,所以选它最顺。

安装 Vitest 并配置 test 命令 ​

怎么做:先安装,再在 package.json 里加一条脚本命令。

bash
pnpm install vitest -D
json
{
  "scripts": {
    "test": "vitest"
  }
}

执行这条命令后,Vitest 会去检查项目中所有符合命名规则的测试文件,并运行其中的测试用例。

易错点:安装要加 -D(开发依赖),因为它只在开发阶段使用,不进入生产构建。

测试文件放哪里、叫什么名字 ​

怎么想:测试文件要和被测代码对应起来,方便定位。

怎么做:比如要测 utils 下的 filter.ts,就在 utils 下新建一个 test 目录,在里面新建 filter.test.ts。后缀必须是 .test.ts,Vitest 才会识别。文件建好后图标会变成一个“化验瓶”样式,说明编辑器已经把它当成测试文件了。

你也可以把所有测试用例统一放在最外层的 test 目录,两种方式都行;这里为了就近对应,直接放在 utils 下。

用 test 和 expect 写第一个用例 ​

Vitest 导出两个核心函数:test 用来声明一个测试用例,expect 用来做断言(预测)。

ts
import { test, expect } from 'vitest'
import { AliasTime } from '@/enums'
import { getAliasTimeText } from '../filter'

test('getAliasTimeText 传入 month 应返回一个月内', () => {
  // 输入:month
  const text = getAliasTimeText(AliasTime.month)
  // 预期输出:一个月内
  expect(text).toBe('一个月内')
})

推导过程:这个用例的预期值从哪来?回到 getAliasTimeText 函数内部看它的 timeOptions 映射——如果传入的是 month,对应的文案就是“一个月内”。所以预期值就是“一个月内”,不是随便写的。

怎么做:test 第一个参数是用例描述,第二个参数是回调,回调里完成“取值 → 断言”。

易错点:断言不要写成“我觉得应该是”,要去源码里确认映射关系再写预期值,否则测试本身就成了 bug。

用测试捕获无意引入的 bug ​

写完执行:

bash
pnpm test

终端会打印日志,告诉你 filter.test.ts 有 1 个测试用例通过。

而且这条命令启动后会监听文件变化,你改代码它就自动重跑。这个特性正好用来演示测试的价值:

  1. 再加一个用例,取 week 对应的文案,预期是“一周内”,保存后通过。
  2. 把预期故意改成“一周”,保存——测试立刻报错,提示你预期是“一周”,实际返回的是“一周内”,用例未通过。改回“一周内”,又通过。
  3. 模拟真实场景:同事在别的页面想显示“一周”,顺手把函数里的“一周内”改成了“一周”,保存后测试立刻变红。他会发现这里早有人写了测试,说明这么写是有原因的,从而发现问题。

这类问题 TypeScript 检查不到,因为类型没变,变的只是逻辑。

第二个函数同理:

ts
test('getConsultFlagText 传入 0 应返回没就诊过', () => {
  const text = getConsultFlagText(0)
  expect(text).toBe('没就诊过')
})

0 代表没有就诊过,所以预期输出是“没就诊过”。这里调试时踩过一个坑:预期写成了“没有就诊过”,多了一个“过”字,测试直接失败——实际返回的是“没就诊过”。字符串断言是逐字比较的,差一个字都不行。

函数颗粒度决定测试难度 ​

toBe 用于判断简单类型的值是否相等;如果断言对象是复杂类型(数组、对象),它比较的是引用地址,这时要用 toEqual 比较值(组件测试中会用到)。

更重要的是这条经验:上面两个函数都很简单,所以容易写预期。如果函数非常复杂、依赖很多,你连“预期输出是什么”都很难说清。所以想让项目能测试,函数就要拆得颗粒度更小。颗粒度越小,输出越可预测,测试越准确;将来真被引入 bug,测试也能第一时间定位到具体函数。

代码示例 ​

1. mock 配置 + 生成数据 ​

ts
// vite.config.ts
/// <reference types="vitest/config" />
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { viteMockServe } from 'vite-plugin-mock'

export default defineConfig({
  plugins: [
    vue(),
    viteMockServe({
      mockPath: 'mock',     // mock 代码所在目录
      localEnabled: true    // 本地开发环境开启 mock
    })
  ],
  test: {
    environment: 'happy-dom' // 组件测试需要 DOM 环境
  }
})
ts
// mock/index.ts
import type { MockMethod } from 'vite-plugin-mock'
import Mock from 'mockjs'

const data: any[] = []

for (let i = 0; i < 10; i++) {
  data.push({
    id: Mock.mock('@id'),                 // 唯一标识
    title: Mock.mock('@ctitle(5, 10)'),   // 中文标题 5~10 字
    content: Mock.mock('@ctitle(10, 20)'),// 中文内容 10~20 字
    avatar: Mock.mock("@image('100x100')"), // 100 x 100 的随机图片
    createTime: Mock.mock('@datetime'),   // 随机时间
    status: Mock.mock('@integer(0, 1)'),  // 状态只能是 0 或 1
    type: Mock.mock('@integer(1, 3)')     // 1 系统 / 2 交易 / 3 聊天
  })
}

export default [
  {
    url: '/api/message/list',
    method: 'get',
    timeout: 1000, // 模拟网络延迟
    response: () => ({
      code: 200,
      message: 'success',
      data
    })
  }
] as MockMethod[]

要点:

  • viteMockServe 必须配 localEnabled: true(生产用 prodEnabled),且请求地址要带上本地开发域名(如 http://localhost:5173/api/message/list),否则命中的还是线上真实接口,这是最容易踩的坑。
  • data 先声明数组,再在 for 循环里 push 10 条,字段名必须和接口文档完全一致,否则页面拿到的字段是 undefined。
  • Mock 规则记忆:@id 唯一 ID、@ctitle(5,10) 中文标题、@image('100x100') 图片(宽 x 高,中间是小写 x,写在双引号字符串里要用单引号)、@datetime 时间、@integer(min, max) 限定范围数字。
  • status 用 @integer(0, 1)、type 用 @integer(1, 3),不限定范围就会造出接口文档里不存在的值。
  • 接口开发好之后,只需把调用处的 mock 域名去掉,就能无缝切回线上接口。

2. 单元测试(vitest) ​

ts
// utils/filter.test.ts
import { test, expect } from 'vitest'
import { getIslandsTimeText, getConsultFlagText, IslandsTime } from './filter'

test('getIslandsTimeText', () => {
  const text = getIslandsTimeText(IslandsTime.month)
  expect(text).toBe('一个月内')

  const text2 = getIslandsTimeText(IslandsTime.week)
  expect(text2).toBe('一周内')
})

test('getConsultFlagText', () => {
  const text = getConsultFlagText(0)
  expect(text).toBe('没就诊过')
})

要点:

  • 先 pnpm i vitest -D 安装,再在 package.json 加 "test": "vitest",执行后会自动扫描项目里所有 *.test.ts 文件。
  • 测试文件命名必须是 xxx.test.ts,放在被测文件同级的 test 目录或项目根 test 目录都可以。
  • test(描述, 回调) 声明一个用例,expect(实际值).toBe(预期值) 做断言;简单类型 toBe 比值,数组/对象要用 toEqual(toBe 比的是引用地址)。
  • 命令启动后处于 watch 模式,改代码会不停重跑;故意把预期写成 '一周'(实际返回 '一周内')用例就会失败——这正是它能拦住同事误改文案、而 TS 静态检查拦不住的地方。

3. 组件测试(@vue/test-utils + happy-dom) ​

ts
// components/test/components.test.ts
import { test, expect } from 'vitest'
import { render } from '@vue/test-utils'
import CPRadioButton from '../CPRadioButton.vue'

test('CPRadioButton', async () => {
  // 1. 单独挂载组件,传入默认 props
  const wrapper = render(CPRadioButton, {
    props: {
      options: [
        { label: '选项一', value: 1 },
        { label: '选项二', value: 2 }
      ],
      modelValue: 1
    }
  })

  // 2. 测试渲染
  expect(wrapper.getByText('选项一')).toBeTruthy()
  expect(wrapper.getByText('选项二')).toBeTruthy()

  // 3. 测试 props 驱动激活(组件用 active 类名标记选中)
  expect(wrapper.queryByText('选项一').classes()).toContain('active')

  // 4. 测试用户点击 -> 自定义事件与传值
  await wrapper.queryByText('选项二').trigger('click')
  expect(wrapper.emitted('update:modelValue')![0]).toEqual([2])

  // 5. 把事件抛出的值回传给组件,验证视图更新(即 v-model 闭环)
  await wrapper.rerender({ modelValue: 2 })
  expect(wrapper.queryByText('选项二').classes()).toContain('active')
})

要点:

  • 需要安装 @vue/test-utils 和 happy-dom;不配 test.environment: 'happy-dom' 会直接报 document is not defined,同时在 vite.config.ts 顶部加 /// <reference types="vitest/config" /> 才有 test 字段的类型提示。
  • render(组件, { props }) 返回的 wrapper 是组件容器,getByText 找不到会抛错(用于断言"一定有"),queryByText 找不到返回空(用于拿到元素再取 classes())。
  • emitted('update:modelValue') 返回的是"每次触发对应一个参数数组"的数组,所以写 [0] 取第一次触发、再 toEqual([2]) 比对参数;用 toBe([2]) 会因引用不同而失败。
  • rerender 返回 Promise,必须 await 等组件更新完再断言,否则读到的是旧 DOM,用例会莫名失败。
  • 一个用例覆盖四件事:挂载、渲染、props 更新视图、交互触发自定义事件并回传值,第 4、5 步合起来就是 v-model 的测试。

常见错误 ​

  1. 改完 mock 文件,浏览器里数据还是旧的 现象:index.ts 里 push 了 10 条数据,刷新页面拿到的还是空数组或旧字段。 原因:mock 配置文件在 dev server 启动时被读取并注册成中间件,运行时修改不会热替换接口注册。 修法:改完 mock 目录下的文件后重启 pnpm dev;把「改 mock → 重启 → 刷新」当成固定动作,别在浏览器里反复刷新找原因。

  2. 请求打到了线上真实接口,mock 完全没生效 现象:Network 面板里请求的域名是线上域名,返回真实数据;控制台没有 mock 相关日志。 原因:mock 只挂在本地 dev server 上,用线上域名请求自然不会经过它;或 vite.config.ts 里 mock 插件未启用 / prodEnabled 与 enable 配置不当。 修法:请求地址换成当前本地开发域名;把 baseURL 抽成环境变量,切换环境只改一处;确认插件配置后再排查代码。

  3. 组件测试一跑就报 document is not defined 现象:mount 那一行直接抛错,用例全部失败。 原因:测试默认跑在 Node 环境,没有 DOM,渲染组件时访问 document 就崩。 修法:安装 happy-dom(或 jsdom),在 vite.config.ts 的 test.environment 里指定;同时用三斜线指令引入 vitest/config 的类型,让 test 字段有提示不报红。

  4. 断言数组 / 对象永远不通过 现象:明明打印出来值一样,expect(res).toBe([2]) 却红灯。 原因:toBe 走的是引用比较,两个内容相同的数组地址不同,必然不等。 修法:基本类型用 toBe,数组/对象用 toEqual;只关心「包含某元素」用 toContain,只关心部分字段用 toMatchObject。

  5. 触发点击后立刻断言,结果时好时坏 现象:trigger('click') 下一行读 DOM 状态或事件参数,偶尔拿到旧值。 原因:trigger、setProps 返回 Promise,视图更新在微任务之后才完成。 修法:await wrapper.trigger('click')、await wrapper.setProps({...}),必要时补 await nextTick();凡是「改数据 → 看 DOM」的链路都要等。

  6. queryByText 找不到元素,后面接 .classList 直接报错 现象:元素不存在时抛 Cannot read properties of null,看不出是用例断言失败还是代码写错。 原因:queryBy* 查不到返回 null,而 getBy* 查不到会直接抛错;用 queryBy 后没做空判断。 修法:正向用例用 getByText 让失败信息更明确;负向用例(断言「不存在」)才用 queryBy,且只做 toBeNull() 判断,不再往下取属性。

练习 ​

  1. 给消息列表 mock 增加分页能力:接收 page、pageSize 两个 query 参数,返回 { list, total }。要求 id 全局唯一、createTime 为随机时间、status 只出现 0/1、type 只出现 1/2/3。写完后用浏览器 Network 面板逐条核对字段与接口文档是否完全一致。

  2. 把「修改 mock 文件 → 不重启 → 刷新页面 → 数据没变 → 重启后正常」这个排查过程完整走一遍,并记录每一步的观察点(控制台输出、Network 请求域名、返回体),整理成一份 5 行以内的排错清单。

  3. 为 getConsultFlagText 写一组单元测试,至少覆盖 3 个输入:未就诊、已就诊、以及一个非法/边界输入。要求每个用例名能说清「输入什么、期望什么」,跑 pnpm test 时看红灯信息能否一眼定位问题。

  4. 为单选按钮组件补两个用例:① 点击后断言触发的自定义事件名与传出的值;② 断言「点选项二后,选项二的按钮被激活而选项一没有」。第二题必须用 await 处理更新,故意去掉 await 观察失败表现并记录差异。

  5. 故意把源码中某个文案或分支逻辑改错(模拟同事误改),观察测试是否红灯、TS 是否报错,据此说明「类型检查」和「单元测试」各自能拦住什么问题。改完记得还原。

课件图示 ​

课件第23页:扩展-单元测试 单元测试:检查给定函数、类或组合式函数的输入是否产生预期的输出或

课件第21页:扩展-mock数据 背景:在开发过程中后台未开发好接口,前端要推进业务进行需要模

课件第25页:扩展-组件测试 对于 视图 的测试:根据输入 prop 和插槽断言渲染 输出是否

课件第3页:三方登录-QQ登录流程 去QQ登录且回跳 使用openId登录 验证码 comp

课件第5页:三方登录-去QQ登录且回跳 配置host和白名单 设置跳转QQ地址 hosts

课件第7页:三方登录-使用openId登录 获取openId 给QC添加类型 使用openI

原视频对照 ​

#视频时长
1扩展-mock数据-02-生成数据11:11
2扩展-mock数据-02-生成数据11:11
3扩展-单元测试13:52
4扩展-单元测试13:52
5扩展-组件测试20:39
6扩展-组件测试20:39

小结 ​

  1. mock 的定位是「本地开发的临时后端」:字段必须与接口文档逐一对齐,否则业务代码写完上线还要返工,收益归零。
  2. mock 生效与否,先看请求域名再看配置——90% 的「mock 不生效」是请求没走本地 dev server,而不是插件写错。
  3. mock 数据要「随机但受约束」:随机保证真实感,范围约束(status 只能 0/1、type 只能 1/2/3)保证不会造出线上不可能存在的脏数据。
  4. 类型检查管「形状」,测试管「行为」:前者拦字段写错、参数类型不符,后者拦逻辑分支写反、文案改动、交互失效,二者不可互相替代。
  5. 测试越早引入成本越低:函数一旦膨胀、依赖变多,可测性就急剧下降;反过来,好测的代码往往就是拆分合理的代码。
  6. 组件测试的落点是四件事:能否挂载、初始渲染是否正确、props 是否驱动视图、交互是否触发事件并回传正确值——其中「事件 + 回传值 + 视图更新」串起来就是一次完整的双向绑定验证。
  7. 写断言的纪律:断言要能说出「我期望什么」,失败信息要能直接定位问题;选择 toBe / toEqual / toContain 时想清楚比的是引用、结构还是成员。
  8. 测试文件要进版本库并接入 CI,让它成为「改错就会响的警报」,否则只是本地跑过一次的演示脚本。