Vue 组合式函数的测试方法(译)

大家好,这里是大家的林语冰。本期《前端翻译计划》共享的是 Vue 周报推荐的一篇关于测试 Vue 组合式函数的博客。

免责声明

本文属于是语冰的直男翻译了属于是,略有删改,仅供粉丝参考,英文原味版请临幸 How to Test Vue Composables

引言

在本文中,我想辅助您更好地理解如何在 Vue 中测试组合式函数。如今,我们的大部分业务逻辑或 UI 逻辑通常封装在组合式函数中,这就是我认为了解如何测试它们十分重要重要的原因。

定义

在深入探讨主题之前,了解若干有关测试的基本概念十分重要。这些基础知识将有助于阐明测试 Vue 组合式函数在更广泛的软件测试领域中的位置。

组合式函数

Vue 组合式函数是可复用的组合函数,用于封装和管理响应式状态和逻辑。它们允许以灵活的方式跨组件组织和重用代码,增强模块化和可维护性。

测试金字塔

测试金字塔是一个概念性的隐喻,它说明了不同类型测试的理想平衡。它建议使用一大坨单元测试,辅以一小坨集成测试集,并添加更小坨端到端测试集。这种结构确保了高效的测试覆盖率。

单元测试以及测试组合式函数如何成为单元测试

单元测试是指单独测试单个代码单元的做法。在 Vue 的上下文中,测试组合式函数是单元测试的一种形式。它涉及严格验证这些隔离的、可复用代码块的功能,确保它们在没有外部依赖的情况下正常运行。

测试组合式函数

Vue 组合式函数本质上是函数,利用了 Vue 的响应式系统。鉴于这种独特的性质,我们可以将组合式函数分类为不同的类型。一方面,由于自治组合式函数(Independent Composables)的隔离性,可以直接测试它们。另一方面,我们有依赖组合式函数(Dependent COmposables),它们只有集成到组件中才能奏效。接下来我将深入探讨这些不同的类型,为每种类型提供示例,并指导您为这两种类型制定有效的测试策略。

自治组合式函数

自治组合式函数门使用 Vue 的响应式 API。这些可组合项独立于 Vue 组件实例运行,因此易于测试。

示例和测试策略

下面是一个自治组合式函数的示例,它计算两个响应值的总和:

ts 复制代码
useSum(a: Ref<number>, b: Ref<number>): ComputedRef<number> {
  return computed(() => a.value + b.value)
}

要测试这个组合式函数,您可以直接调用它并断言其返回状态:

js 复制代码
describe('useSum', () => {
  it('correctly computes the sum of two numbers', () => {
    const num1 = ref(2)
    const num2 = ref(3)
    const sum = useSum(num1, num2)

    expect(sum.value).toBe(5)
  })
})

此测试通过传递响应式引用并断言计算结果来直接检查 useSum 的功能。

依赖组合式函数

依赖组合式函数的差异在于它们依赖于 Vue 的组件实例。它们通常利用生命周期钩子或上下文等功能进行操作。这些组合式函数是组件的组成部分,需要一种独特的测试方法,因为不同于自治可组合项。

示例和用法

一个示例性的依赖组合式函数是 useLocalStorage。这个组合式函数有助于与浏览器的 localStorage 进行交互,并利用 onMounted 生命周期钩子初始化:

ts 复制代码
function useLocalStorage<T>(key: string, initialValue: T) {
  const value = ref(initialValue)

  function loadFromLocalStorage() {
    const storedValue = localStorage.getItem(key)
    if (storedValue !== null) {
      value.value = JSON.parse(storedValue)
    }
  }

  onMounted(loadFromLocalStorage)

  watch(value, newValue => {
    localStorage.setItem(key, JSON.stringify(newValue))
  })

  return { value }
}

export default useLocalStorage

举个栗子,可以在组件中使用该组合式函数来创建持久计数器:

这里的主要好处是响应式 count 属性与 localStorage 的无缝同步,确保跨会话的持久性。

测试策略

为了有效地测试 useLocalStorage,尤其是考虑到 onMounted 生命周期,我们开始面临挑战。让我们从基本的测试设置开始:

js 复制代码
describe('useLocalStorage', () => {
  it('should load the initialValue', () => {
    const { value } = useLocalStorage('testKey', 'initValue')
    expect(value.value).toBe('initValue')
  })

  it('should load from localStorage', async () => {
    localStorage.setItem('testKey', JSON.stringify('fromStorage'))
    const { value } = useLocalStorage('testKey', 'initialValue')
    expect(value.value).toBe('fromStorage')
  })
})

在这里,首个测试将通过,断言组合式函数初始化为给定的 initialValue。但是,第二个测试期望从 localStorage 加载预先存在的值会失败。之所以出现挑战,是因为 onMounted 在测试期间没有触发生命周期钩子。为了搞定此问题,我们需要重构我们的组合式函数或测试设置,模拟组件安装过程。

使用 withSetup 辅助函数增强测试

为了便于测试依赖 Vue 生命周期钩子的组合式函数,我们开发了一个 withSetup 高阶函数。此工具允许我们编程创建一个 Vue 组件上下文,主要聚焦组合式函数通常使用的 setup 生命周期函数。

withSetup 简介

withSetup 旨在模拟 Vue 组件的 setup 函数,使我们能够在密切模仿其实际使用的环境中测试组合式函数。该函数接受一个组合式函数,并返回组合式函数的结果和一个 Vue App 实例。此 setup 允许综合测试,包括生命周期和响应性功能。

ts 复制代码
import type { App } from 'vue'
import { createApp } from 'vue'

export function withSetup<T>(composable: () => T): [T, App] {
  let result: T
  const app = createApp({
    setup() {
      result = composable()
      return () => {}
    }
  })
  app.mount(document.createElement('div'))
  return [result, app]
}

在此实现中,withSetup 挂载在一个最小的 Vue App,并在 setup 阶段执行提供的组合式函数。这种方法允许我们捕获组合式函数的输出并将其与 App 实例一起返回,以便进一步测试。

在测试中使用 withSetup

借助 withSetup,我们可以增强组合式函数的测试策略,比如 useLocalStorage,确保它们即使在依赖生命周期钩子时也能如期运行:

js 复制代码
it('should load the value from localStorage if it was set before', async () => {
  localStorage.setItem('testKey', JSON.stringify('valueFromLocalStorage'))
  const [result] = withSetup(() => useLocalStorage('testKey', 'testValue'))
  expect(result.value.value).toBe('valueFromLocalStorage')
})

这个测试演示了 withSetup 如何让组合式函数像常规 Vue 组件的一部分一样执行,确保 onMounted 生命周期钩子如期触发。此外,强大的 TypeScript 支持通过提供清晰的类型推断和错误检查来增强开发体验。

完结撒花

在测试 Vue 组合式函数的探索中,我们发现了两个不同的类别:自治组合式函数和依赖组合式函数。自治组合式函数是独立的,可以像常规函数一样进行测试,展示了简单的测试程序。同时,依赖组合式函数与 Vue 的组件系统和生命周期钩子纠缠不清,需要一种更细致的方法。对于这些,我们学习了利用辅助函数(比如 withSetup)来模拟组件上下文的有效性,从而实现综合测试。

您现在收看的是前端翻译计划,学废了的小伙伴可以订阅此专栏合集,我们每天佛系投稿,欢迎持续关注前端生态。谢谢大家的点赞,掰掰~

相关推荐
fruge9 分钟前
纯css制作声波扩散动画、js+css3波纹催眠动画特效、【css3动画】圆波扩散效果、雷达光波效果完整代码
javascript·css·css3
neter.asia18 分钟前
vue中如何关闭eslint检测?
前端·javascript·vue.js
十一吖i36 分钟前
前端将后端返回的文件下载到本地
vue.js·elementplus
光影少年38 分钟前
vue2与vue3的全局通信插件,如何实现自定义的插件
前端·javascript·vue.js
Rattenking43 分钟前
React 源码学习01 ---- React.Children.map 的实现与应用
javascript·学习·react.js
熊的猫2 小时前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js
mosen8682 小时前
Uniapp去除顶部导航栏-小程序、H5、APP适用
vue.js·微信小程序·小程序·uni-app·uniapp
别拿曾经看以后~3 小时前
【el-form】记一例好用的el-input输入框回车调接口和el-button按钮防重点击
javascript·vue.js·elementui
川石课堂软件测试3 小时前
性能测试|docker容器下搭建JMeter+Grafana+Influxdb监控可视化平台
运维·javascript·深度学习·jmeter·docker·容器·grafana
JerryXZR4 小时前
前端开发中ES6的技术细节二
前端·javascript·es6