vue-35(使用 Jest 和 Vue Test Utils 设置测试环境)

发布于:2025-07-02 ⋅ 阅读:(15) ⋅ 点赞:(0)

使用 Jest 和 Vue Test Utils 设置测试环境

设置一个可靠的测试环境对于确保 Vue.js 应用的可靠性和可维护性至关重要。本课程将指导你配置 Jest(一个流行的 JavaScript 测试框架)和 Vue Test Utils(一个专门为测试 Vue 组件设计的库)。在本课程结束时,你将具备为你的 Vue 组件编写有效单元测试的坚实基础,涵盖属性、事件和方法等方面。这将为你后续课程中将要涵盖的高级测试技术(如模拟依赖项和端到端测试)做好准备。

安装 Jest 和 Vue Test Utils

第一步是在你的 Vue 项目中作为开发依赖项安装 Jest 和 Vue Test Utils。打开你的终端并导航到你的项目目录。然后,使用 npm 运行以下命令:

npm install --save-dev @vue/test-utils jest babel-jest @babel/preset-env

或者,如果你更喜欢使用 yarn:

yarn add --dev @vue/test-utils jest babel-jest @babel/preset-env

解释:

  • @vue/test-utils: 这是官方的 Vue 测试工具库,提供在测试环境中挂载和交互 Vue 组件的实用方法。
  • jest: 这是一个广泛使用的 JavaScript 测试框架,提供测试运行器、断言和模拟等功能。
  • babel-jest: 这是一个转换器,允许 Jest 理解现代 JavaScript 语法,包括 ES 模块和 JSX。
  • @babel/preset-env: 一个 Babel 预设,让你能够使用最新的 JavaScript,而无需手动管理目标环境支持哪些语法转换(插件)。这确保了你的代码与你想支持的网络浏览器兼容。

配置 Babel 以用于 Jest

Jest 需要 Babel 来转译你的 Vue 组件和 JavaScript 代码。在项目的根目录中创建一个 babel.config.js 文件,并包含以下内容:

module.exports = {
  presets: [
    ['@babel/preset-env', { targets: { node: 'current' } }]
  ]
}

解释:

  • module.exports: 这导出了配置对象,使其对 Babel 可用。
  • presets: 这个数组指定要使用的 Babel 插件。
  • @babel/preset-env: 此预设会根据您的目标环境(在本例中为当前的 Node.js 版本)自动确定所需的转换。
  • targets: { node: 'current' }: 这告诉 Babel 以当前 Node.js 版本为目标,确保与 Jest 的测试环境兼容。

配置 Jest

接下来,你需要配置 Jest。在项目的根目录中创建一个 jest.config.js 文件,内容如下:

module.exports = {
  moduleFileExtensions: [
    'js',
    'jsx',
    'json',
    'vue'
  ],
  transform: {
    '^.+\\.vue$': '@vue/vue3-jest',
    '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
    '^.+\\.jsx?$': 'babel-jest'
  },
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1'
  },
  snapshotSerializers: [
    'jest-serializer-vue'
  ],
  testMatch: [
    '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
  ],
  testEnvironment: 'jsdom',
  transformIgnorePatterns: ['/node_modules/'],
};

解释:

  • moduleFileExtensions: 这个数组指定 Jest 应该识别为模块的文件扩展名。
  • transform: 这个对象定义了 Jest 应该如何转换不同类型的文件。
    • ^.+\\.vue$: 这个正则表达式匹配 Vue 文件。使用 @vue/vue3-jest 转换器来处理这些文件。如果你在使用 Vue 2,应该使用 vue-jest 代替。
    • .+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$ : 这个正则表达式匹配各种资源文件。使用 jest-transform-stub 转换器将这些文件替换为空模块,因为它们实际的内容通常对单元测试无关紧要。
    • ^.+\\.jsx?$: 此正则表达式匹配 JavaScript 和 JSX 文件。使用 babel-jest 转换器处理这些文件。
  • moduleNameMapper: 该对象将模块名称映射到文件路径。
    • ^@/(.*)$': '<rootDir>/src/$1': 这会将以 @/ 开头的任何模块映射到 src 目录中的相应路径。这对于解析使用 @ 别名的导入很有用。
  • snapshotSerializers: 这个数组指定要使用的快照序列化器。
    • jest-serializer-vue: 这个序列化器以更易读的方式格式化 Vue 组件快照。如果你使用的是 Vue 2,你应该使用 jest-serializer-vue
  • testMatch: 这个数组指定了 Jest 应该考虑为测试文件的文件。
    • **/tests/unit/**/*.spec.(js|jsx|ts|tsx) : 这匹配 .spec.js.spec.jsx.spec.ts 或 .spec.tsx 扩展名的文件,这些文件位于 tests/unit 目录中。
    • **/__tests__/*.(js|jsx|ts|tsx) : 这匹配 __tests__ 目录中的文件。
  • testEnvironment: 指定测试环境。 jsdom 是一个 JavaScript 实现的 DOM,适用于测试与 DOM 交互的 Vue 组件。
  • transformIgnorePatterns: 防止 Jest 转换 node_modules 中的文件。

更新 package.json 中的测试脚本

在您的 package.json 文件中添加一个测试脚本,以便轻松运行您的测试:

{
  "scripts": {
    "test": "jest"
  }
}

现在您可以使用命令 npm test 或 yarn test 运行您的测试。

创建您的第一个测试

在项目的根目录下创建一个 tests/unit 目录。在这个目录内,创建一个名为 example.spec.js 的文件。将以下代码添加到这个文件中:

import { shallowMount } from '@vue/test-utils';
import ExampleComponent from '@/components/ExampleComponent.vue';

describe('ExampleComponent', () => {
  it('renders props.msg when passed', () => {
    const msg = 'Hello Jest';
    const wrapper = shallowMount(ExampleComponent, {
      props: { msg }
    });
    expect(wrapper.text()).toContain(msg);
  });
});

解释:

  • import { shallowMount } from '@vue/test-utils' : 从 Vue Test Utils 中导入 shallowMount 函数。shallowMount 创建组件的浅层包装器,这意味着任何子组件都会被存根化。
  • import ExampleComponent from '@/components/ExampleComponent.vue' : 导入你想测试的组件。注意使用 @ 别名,该别名在 jest.config.js 中配置。
  • describe('ExampleComponent', () => { ... }) : 为 ExampleComponent 定义一个测试套件。
  • it('renders props.msg when passed', () => { ... }) : 在测试套件中定义一个单独的测试用例。
  • const msg = 'Hello Jest': 定义 msg 属性的值。
  • const wrapper = shallowMount(ExampleComponent, { props: { msg } }) : 创建一个浅包装器,包装了 ExampleComponent,并带有 msg 属性。
  • expect(wrapper.text()).toContain(msg) : 断言组件渲染的文本包含 msg 属性的值。

创建示例组件

在项目的根目录下创建一个 src/components 目录。在这个目录中,创建一个名为 ExampleComponent.vue 的文件。将以下代码添加到这个文件中:

<template>
  <div>
    <p>{{ msg }}</p>
  </div>
</template>

<script>
export default {
  props: {
    msg: {
      type: String,
      required: true
    }
  }
}
</script>

解释:

  • <template>: 定义组件的模板,其中包含一个段落元素,用于显示 msg 属性的值。
  • <script>: 定义组件的 JavaScript 逻辑,包括将 props 选项声明为必需的字符串的 msg prop。

运行测试

现在,使用命令 npm test 或 yarn test 运行测试。你应该看到输出表明测试已通过。

为组件编写单元测试:属性、事件和方法

现在你已经设置了一个基本的测试环境,让我们来探索如何为 Vue 组件的不同方面编写单元测试,包括 props、事件和方法。

测试属性

如前例所示,您可以通过在挂载组件时使用 shallowMount 或 mount 将其传递给组件来测试属性(我们稍后会讨论两者的区别)。然后,您可以使用断言来验证组件是否正确渲染了属性。

示例:

import { shallowMount } from '@vue/test-utils';
import MyComponent from '@/components/MyComponent.vue';

describe('MyComponent', () => {
  it('renders the correct title', () => {
    const title = 'My Awesome Title';
    const wrapper = shallowMount(MyComponent, {
      props: { title }
    });
    expect(wrapper.find('h1').text()).toBe(title);
  });
});

解释:

  • wrapper.find('h1'): 在组件的渲染输出中查找 h1 元素。
  • wrapper.find('h1').text(): 获取 h1 元素的文本内容。
  • toBe(title): 断言 h1 元素的文本内容与预期标题相等。

测试事件

你可以测试组件在特定操作发生时是否发出正确的事件。使用 wrapper.emitted() 来检查已发出哪些事件。

示例:

import { shallowMount } from '@vue/test-utils';
import MyButton from '@/components/MyButton.vue';

describe('MyButton', () => {
  it('emits a "click" event when clicked', async () => {
    const wrapper = shallowMount(MyButton);
    await wrapper.find('button').trigger('click');
    expect(wrapper.emitted()).toHaveProperty('click');
  });

  it('emits the correct payload with the "click" event', async () => {
    const wrapper = shallowMount(MyButton);
    await wrapper.find('button').trigger('click');
    const emittedEvent = wrapper.emitted('click');
    expect(emittedEvent[0]).toEqual(['payload data']);
  });
});

解释:

  • wrapper.find('button'): 在组件的渲染输出中查找 button 元素。
  • wrapper.find('button').trigger('click') : 在 button 元素上触发点击事件。
  • wrapper.emitted(): 返回一个包含组件已触发所有事件的对象。
  • toHaveProperty('click'): 断言 click 事件已被触发。
  • wrapper.emitted('click'): 返回一个数组,其中每个内部数组包含传递给该事件的 emit 函数的参数。
  • toEqual(['payload data']): 断言第一个发出的 click 事件使用 ['payload data'] 作为负载发出。

这是相应的 MyButton.vue 组件:

<template>
  <button @click="handleClick">Click me</button>
</template>

<script>
export default {
  methods: {
    handleClick() {
      this.$emit('click', 'payload data');
    }
  }
}
</script>

测试方法

你可以通过组件实例来访问并测试组件的方法。

示例:

import { shallowMount } from '@vue/test-utils';
import MyComponent from '@/components/MyComponent.vue';

describe('MyComponent', () => {
  it('increments the count when increment method is called', async () => {
    const wrapper = shallowMount(MyComponent);
    const vm = wrapper.vm; // Access the Vue instance
    vm.increment();
    await vm.$nextTick(); // Wait for the DOM to update
    expect(wrapper.find('p').text()).toBe('Count: 1');
  });
});

解释:

  • wrapper.vm: 访问组件的 Vue 实例。
  • vm.increment(): 调用组件实例上的 increment 方法。
  • await vm.$nextTick(): 等待状态变化后 DOM 更新。这很重要,因为 Vue 异步更新 DOM。
  • expect(wrapper.find('p').text()).toBe('Count: 1') : 断言在调用 increment 方法后,p 元素的文本内容等于预期值。

这是对应的 MyComponent.vue 组件:

<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    };
  },
  methods: {
    increment() {
      this.count++;
    }
  }
}
</script>

ShallowMount 与 Mount

Vue Test Utils 提供了两种主要的挂载组件的方法:shallowMount 和 mount。理解它们之间的区别对于编写高效和有效的测试至关重要。

shallowMount

shallowMount 创建组件的  封装。这意味着仅渲染组件本身,而任何子组件将被替换为占位符。这对于隔离正在测试的组件并防止测试受子组件变化的影响很有用。shallowMount 通常比 mount 更快、更高效。

mount

mount 创建组件的 完整 包装。这意味着组件及其所有子组件都会被渲染。这对于测试组件之间的集成并验证它们能否正确协同工作非常有用。mount 通常比 shallowMount 慢且更耗费资源。

何时使用哪个:

  • 使用 shallowMount 进行单元测试,以隔离单个组件。
  • 使用 mount 进行集成测试,以验证组件间的交互。

网站公告

今日签到

点亮在社区的每一天
去签到