本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2022-06-06
在编写测试之前,你应该先清楚的知道确定规范。
高级规范:需求是从用户的视角对应用程序进行的模糊描述,你需要不断的对需求进行澄清以便获得可以诠释产品是如何工作的技术规格说明书。比如,设计稿就回答了页面排版、布局的问题,而需求进一步的分析就能确定代码逻辑(展示多少数据即输出,如何获取数据即输入)等等,这些就是高级规范。
组件规范:对于 Vue,一切皆为组件,当你确定了程序高级规范后,你就需要考虑如何实现它们,代码结构如何构造,组件层级如何设计,父子组件传值通信如何实现,最终组件如何渲染正确的数据,这就像是一个组件契约(当你编写应用程序所使用的组件的时候,你就是在为该组件的行为定义一个契约,如果提供了正确的输入,该组件将履行其契约协议并产生约定的输出。)一个组件契约就像一个组件的 API,组件规范就是对这些 API 的说明。
如何测试组件渲染文本以及如何测试一个特定 DOM 元素渲染正确文本。
<template>
<li>
{{ item.url }}
</li>
</template>
<script>
export default {
props: ['item']
}
</script>
import { shallowMount } from '@vue/test-utils'
import Item from '../Item.vue'
describe('Item.vue', () => {
test('renders item.url', () => {
const item = { // 模拟数据
url: 10,
}
const wrapper = shallowMount(Item, {
propsData: { item } // 使用 prop 传递数据
});
expect(wrapper.text()).toContain(item.url); // 断言 item 可以正常接收并渲染item.url
})
})
find
方法搜索包装器渲染树,返回匹配选择器的第一个匹配节点的包装器。
<!-- 修改template -->
<template>
<li>
<a> {{ item.title }} </a>
{{ item.url }}
</li>
</template>
const a = wrapper.find('a'); // 找到 a 元素的包装器
expect(a.text()).toBe(item.title);
包装器对象有一个 attributes
方法,可以返回组件的属性对象。
<!-- 修改template -->
<template>
<li>
<a :href="item.url" target="_blank">{{ item.title }}</a>
{{ item.url }}
</li>
</template>
const a = wrapper.find('a'); // 找到 a 元素的包装器
expect(a.attributes().href).toBe(item.url);
需要注意的是,要避免下面的这种 Boolean 断言。
expect(a.attributes().href === item.url).toBe(true);
// 因为如果测试不通过,你无法确定是 a 没有 href 属性,还是因为 herf 属性的值不正确
当断言失败的时候,“期望值为 true,返回值为 false”,仅这样的信息并不能清楚的说明测试为什么失败。替代 Boolean 断言的方法是使用富有表达力的 值断言,即使失败,提示的信息也会更友好。
就像 find
方法是是面向 document.querySelector
一样,findAll
方法是面向 document.querySelectorAll
的。它会在渲染输出中搜搜与选择器匹配的节点,并返回一个包含匹配节点的包装器的类数组对象。
import { shallowMount } from '@vue/test-utils';
import ItemList from '../ItemList.vue';
import Item from '../Item.vue';
describe('ItemList.vue', () => {
test('renders an Item for each item in window.items', () => {
window.items = [{}, {}, {}]; // mock 数据的个数
const wrapper = shallowMount(ItemList);
expect(wrapper.findAllComponents(Item)).toHaveLength(window.items.length);
// findAll find 针对普通元素 findAllComponents findComponent 针对组件
})
})
检查组件实例是否接收到正确的 prop。在这里 Item 组件契约的一部分就是接收正确的数据,如果你不提供薪酬,你就不能指望员工工作。同样,如果你不提供一个 item prop,就不能指望 Item 可以正确渲染。
props
是一个 Vue Test Utils 包装器方法,它返回一个对象,其中更包含一个包装器组件实例及它们的值的 prop。
<!-- ItemList.vue -->
<template>
<div>
<Item
v-for="item in displayItems"
:key="item.id"
:item="item"
>
</Item>
</div>
</template>
const wrapper = shallowMount(ItemList);
const items = wrapper.findAllComponents(Item);
// 注意 这里是 wrappers
items.wrappers.forEach((wrapper, i) => {
expect(wrapper.props().item).toBe(window.items[i]);
})
需要注意的是,子组件一定声明接收这个 props。
什么时候应该测试元素的 class,这个答案取决于不同的情况,比如,当要检查元素是否隐藏,此时是使用 hidden class 和 show class来控制,这时就需要测试元素的 class。 classes
是一个 Vue Test Utils 包装器方法,返回一个 class
数组,类似于 classList
。
test('Is hidden on initial render', () => {
const wrapper = shallowMount(ProgressBar); // 进度条
expect(wrapper.classes()).toContain('hidden'); // toContain 不仅可以比较字符串的值,还可以比较数组中的值
})
每一个包装器都包含一个 element
属性,它是对包装器包含的 DOM 根节点的引用,你可以使用 element
来访问元素的内联样式。
test('initializes with 0% width', () => {
const wrapper = shallowMount(ProgressBar);
expect(wrapper.element.style.width).toBe('0%');
})
在测试中,少即是多,每个额外的单元测试都会增加测试代码与源代码之间的耦合关系,所以当你编写单元测试时,你需要比唐老鸭更吝啬。
为避免这种耦合,请在测试组件输出时记住以下原则:
编写单元测试,是在 编写足够测试 与 不要过度测试之间持续挣扎 的过程,不要太多,不要太少,而是要刚刚好。 所以,你将永远不会为 表现元素 和 静态CSS类 编写测试,你应该在不写单元测试的情况下添加表现样式。