一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情。
为什么要对组件进行单元测试?
单元测试是持续集成的关键部分。它们通过专注于小型、孤立的实体并确保它们始终按预期运行,从而使你的代码更加可靠。你可以自信地迭代你的项目,而不必担心破坏事情。
单元测试不仅限于脚本。 只要你尊重一些好的实践,我们可以单独测试的任何东西都是可单元测试的。这些实践包括单一职责、可预测性和松散耦合。
作为我们应用程序的可重用实体,Vue.js 组件是单元测试的绝佳候选者。我们将使用各种输入和用户交互来测试我们作为单个单元制作的那个,并确保它始终按照我们的预期运行。
在我们开始之前
自最初的教程以来,一些事情发生了变化。Vue CLI 3已发布。Vue Test Utils——官方的 Vue.js 单元测试实用程序库——已经成熟到 beta 版本。在第一个教程中,我们使用了 webpack-simple,这是一个不包含测试功能的原型模板。出于这些原因,最简单的做法是彻底清除并将项目从教程迁移到更新的 Vue.js 安装。
我从第一个教程中重新创建了项目,因此你可以直接从GitHub下载它。然后,导航到解压缩的目录并安装依赖项。
注意: 确保在继续之前安装Node.js :
cd path/to/my/project npm install
然后,运行项目:
npm run serve
查看 Test Utils 和 Jest
在本教程中,我们将使用官方 Vue.js 测试工具包Vue Test Utils以及Facebook 支持的 JavaScript 测试运行器Jest 。
Vue Test Utils 允许你单独安装 Vue 组件并模拟用户交互。它具有测试单文件组件的所有必要实用程序,包括使用 Vue Router 或 Vuex 的那些。
Jest 是一个功能齐全的测试运行器,几乎不需要任何配置。它还提供了一个内置的断言库。
Vue CLI 3(我用来生成样板文件)允许你选择自己喜欢的测试运行程序,并为你设置它。如果你想使用另一个测试运行器(例如Mocha),请安装Vue CLI 3并生成你自己的启动项目。然后,你可以直接从我的样板文件中迁移源文件。
我们应该测试什么?
单元测试的一种常见方法是只关注公共 API(也称为黑盒测试)。通过忽略实现细节,你允许内部更改而无需调整测试。毕竟,你要做的是确保你的公共 API 不会中断。幕后发生的事情是间接测试的,但重要的是公共 API 保持可靠。
这也是Vue Test Utils guides的官方推荐。因此,我们将只测试我们可以从组件外部访问的内容:
- 用户交互
- 道具变化
我们不会直接测试计算的属性、方法或钩子。这些将通过测试公共接口进行隐式测试。
设置规范文件
与常规测试一样,每个组件都有一个规范文件,描述了我们要运行的所有测试。
规范是 JavaScript 文件。按照惯例,它们与正在测试的组件具有相同的名称,并加上一个.spec后缀。
继续创建一个test/unit/Rating.spec.js文件:
// Rating.spec.js
import { shallowMount } from '@vue/test-utils'
import Rating from '@/components/Rating'
describe('Rating', () => { // your tests go here})
我们已经导入了我们的Rating组件和shallowMount. 后者是一个 Vue Test Utils 函数,它允许我们在不安装子组件的情况下安装我们的组件。
describe函数调用包装了我们将要编写的所有测试——它描述了我们的测试套件。它有自己的范围,并且可以自己包装其他嵌套套件。
说得够多了,让我们开始编写测试。
识别测试场景
当我们Rating从外面看时,我们可以看到它做了以下事情:
- 它呈现一个星星列表,该列表等于
maxStars用户传递的道具的值 active它为每个索引小于或等于stars用户传递的道具的星星添加一个类active当用户单击它并在下一个星星上将其删除时,它会在星星上切换类- 它会切换图标
star以及star-o当用户单击星号时 - 如果用户将
hasCounterprop 设置为true,它会呈现一个计数器,如果他们将其设置为 则隐藏它false,并显示文本说明当前活动的最大星数中有多少星。
请注意,我们只从外部查看组件的功能。我们不关心点击星号执行rate方法,或者内部stars数据属性发生变化。我们可以重命名它们,但这不应该破坏我们的测试。
我们的第一个测试
让我们编写我们的第一个测试。我们首先需要手动挂载我们的组件shallowMount,并将其存储在一个变量中,我们将在该变量上执行断言。我们也可以通过propsData属性传递道具,作为一个对象。
挂载的组件是一个带有一些有用的实用方法的对象:
describe('Rating', () => {
const wrapper = shallowMount(Rating, {
propsData: { maxStars: 6, grade: 3 }
}) it('renders a list of stars with class `active` equal to prop.grade',
() => { // our assertion goes here })})
然后,我们可以编写我们的第一个断言:
it('renders a list of stars with class `active` equal to prop.grade',
() => { expect(wrapper.findAll('.active').length).toEqual(3)})
让我们分析一下这里发生了什么。首先,我们使用 Jest 的expect函数,它将我们想要测试的值作为参数。在我们的例子中,我们调用我们的findAll方法wrapper来获取一个active类的所有元素。这将返回一个WrapperArray,它是一个包含 的数组的对象Wrappers。
AWrapperArray有两个属性:(wrappers包含的Wrappers)和length(的数量Wrappers)。后者是我们需要有预期的星星数量。
该expect函数还返回一个对象,我们可以在该对象上调用方法来测试传递的值。这些方法称为匹配器。在这里,我们使用toEqual匹配器并将期望值作为参数传递给它。该方法返回一个布尔值,这是测试期望通过或失败的值。
总而言之,这里我们说我们希望active在包装器中找到的类的元素总数等于 3(我们分配给gradeprop 的值)。
在你的终端中,运行你的测试:
npm run test:unit
你应该看到它通过吗?
有时间再写一些。