如何对你的第一个 Vue.js 组件进行单元测试(中)

140 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情

模拟用户输入

Vue Test Utils 可以轻松模拟真实用户最终在生产中所做的事情。在我们的例子中,用户可以点击星星来切换它们。我们可以在我们的测试中使用该trigger方法伪造这一点,并调度各种事件。

it('adds `active` class on an inactive star when the user clicks it', 
() => {  const fourthStar = wrapper.findAll('.star').at(3)  
fourthStar.trigger('click')  expect(fourthStar.classes()).toContain('active')})

在这里,我们首先使用findAlland获得第四颗星,它在传递的索引处从 aat返回 a (从零开始编号)。然后,我们在上面模拟事件——我们正在模仿用户点击或点击第四颗星的动作。Wrapper``WrapperArray``click

由于我们将gradeprop 设置为 3,因此第四颗星在我们点击之前应该是不活动的,因此点击事件应该使其处于活动状态。在我们的代码中,这由一个类表示active,我们仅在它们被激活时附加在星上。我们通过调用classesstar 上的方法对其进行测试,该方法将其类名作为字符串数组返回。然后,我们使用toContain匹配器来确保active类在这里。

设置和拆卸

由于我们触发了对组件的点击,我们已经改变了它的状态。问题是我们在所有测试中都使用相同的组件。如果我们改变测试的顺序,并将它移动到第一个位置会发生什么?那么第二次测试就会失败。

当涉及到测试时,您不想依赖诸如顺序之类的脆弱事物。测试套件应该是健壮的,并且现有的测试最好不要改变,除非你破坏了 API。

我们想确保我们总是有一个可预测的包装器来执行断言。我们可以通过设置和拆卸功能来实现这一点。这些是帮助我们在运行测试之前初始化事物,然后进行清理的助手。

在我们的例子中,一种方法可能是在每次测试之前创建我们的包装器,然后在之后销毁它。

let wrapper = null
beforeEach(() => {  
wrapper = shallowMount(Rating, 
{    
propsData: {     
maxStars: 6,      grade: 3    
}  })
})
afterEach(() => {  wrapper.destroy()})
describe('Rating', () => {  // we remove the `const wrapper = …` expression  // …}

正如他们的名字所暗示的那样,分别在每次测试之前和之后运行beforeEachafterEach通过这种方式,我们可以 100% 确定我们在运行新测试时使用的是新包装器。

测试的特殊标识符

将选择器混合用于样式和其他目的(例如测试挂钩)绝不是一个好主意。

如果您更改标签名称或类别怎么办?

如果您想要测试的元素(例如在我们的例子中是计数器)上没有特定标识符怎么办?

您不想用在那里无用的类污染您的生产代码。最好有专门的测试钩子,例如专门的数据属性,但仅限于测试期间。这样,最终构建中就不会留下混乱。

处理此问题的一种方法是创建自定义 Vue 指令

Vue 实例有一个directive方法,该方法接受两个参数 - 一个name和一个用于注入 DOM 时组件生命周期的每个钩子的函数对象。 如果您不关心特定的钩子,您也可以传递单个函数。

让我们创建一个名为directivesin的新目录src/,并添加一个test.js文件。我们将导出要在指令中传递的函数。

// test.js
export default (el, binding) => {  // do stuff}

一个指令钩子可以接受多个参数,在我们的例子中,我们只需要前两个:elbindingel参数引用指令绑定到的元素。参数是一个对象,binding其中包含我们在指令中传递的数据。这样我们就可以随心所欲地操纵元素。

export default (el, binding) => {  Object.keys(binding.value).forEach(value => {    el.setAttribute(`data-test-${value}`, binding.value[value])  })}

我们将一个对象传递给我们的指令,因此我们可以生成以 . 开头的数据属性data-test-。在处理函数中,我们遍历 的每个属性binding,并在我们的元素上设置一个基于名称和值的数据属性。