本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-02-26
一个对快照测试简单的解释就是获取代码的快照,并将其与以前保存的快照进行对比。如果新的快照与前一个快照不匹配,测试就会失败。
Jest 快照测试会对比序列化值(serializable value),基本上任何可以转换为字符串的 JavaScript 代码都是序列化值。
expect('value').toMatchSnapshot();
expect(document.querySelector('div')).toMatchSnapshot();
快照测试第一次启动时,Jest 会用传递给 expect 的值来创建快照文件。
test('reders list item correctly', () => {
const wrapper = shollowMount(ListItem);
// 假设 ListItem 就渲染了一个 <li> 标签
expect(wrapper.element).toMatchSnapshot();
})
当你在 Jest 中运行快照测试时,Jest 会使用 expect 调用的 DOM 节点来生成格式化的文件然后存储,会在未来的测试中进行对比,该文件示例如下:
exports[`reders list item correctly`] = `
<li>
ListItem content.
</li>
`
下次运行快照测试时,它会将 expect 调用的新值与快照文件中的保存的值进行比较,如果输出匹配,快照测试将通过。如果失败,快照测试将带着差异(diff) 失败,可帮助查看那个部分已被更改。
如果新值中的内容被更改为了 'Not List'
,那么就会有以下 diff。
- Snapshot
+ Received
<li>
- ListItem content.
+ Not List
</li>
大概的流程图如下:
Jest 可以为你管理快照文件。快照文件是以 .snap
为扩展名,生成在 __snapshots__
中的文件,该目录与测试文件会在同一目录中被创建,可以删除和覆盖之前保存的快照。快照文件是快照测试输出的唯一真实源,因此应该在源代码管理中包含快照文件,以便在不同设备上运行测试时使用。
静态组件指的是总是渲染相同输出的组件,它不接受任何 prop,也没有任何 state。组件中没有任何逻辑,并且总是会渲染相同的 HTML 元素。
为静态组建编写单元测试是没有必要的,但是在最初编写完静态组件并手动测试它之后,想要确保静态组件在未来不会发生更改,单元测试就变得非常有用了。
<!-- 静态组件 Spinner.vue -->
<template>
<transition>
<svg class="spinner" width="44px" height="44px" viewBox="0 0 44 44">
<circle
class="path" fill="none" stroke-width="4" stroke-linecap="round"
cx="22" cy="22" r="20"
>
</circle>
</svg>
</transition>
</template>
快照测试代码如下:
import { shallowMount } from '@vue/test-utils';
import Spinner from '../Spinner.vue';
describe('', () => {
test('', () => {
expect(shallowMount(Spinner).element).toMatchSnapshot();
})
})
运行测试套件后,检查控制台的输出内容,你会发现 Jest 创建了一个 snap 文件,如果测试文件是 src/__tests__/compoents/Spinner.spec.js
,那么对应的 snap 文件就是 src/__tests__/compoents/__snapshots__/Spinner.spec.js.snap
,查看该文件,你会发现 element 的值被写入到了这个文件之中。
如果在这之后修改一下 Spinner.vue
中的 svg 标签的属性,在运行测试,快照测试就会失败,因为它会与之前保存的进行比较。
你也可以使用 --update
标识调用 Jest,重写 snap,这样就不会与之前保存的进行比较,而是直接覆盖,即将本次的快照当做初始快照。
npm run test:unit -- --updateSnapshot
# 或
npm run test:unit -- --u
这行命令会告诉 Jest 去重写所有失败的快照文件,当你想重写多个快照文件时这个方式很实用,但是这个操作比较危险,它可能会意外地添加错误的快照,为了避免错误的快照被添加,你可以使用交互模式启动 Jest,使用一下命令运行交互式更新快照模式。
npm run test:unit -- --watch
当提示符在终端出现时,按 i 键浏览所有失败的快照测试,按 u 键可以使用新值来更新之前保存的快照文件。交互模式是一种同时验证多个快照的安全办法。
这里的动态组件指的是包含逻辑和状态的组件,比如说,点击按钮时它们会传递 prop 或更新数据。
当你为动态组件编写快照测试时,应该尝试捕获尽可能多的不同组合的状态,这样,快照测试将尽可能多地覆盖功能。
假设 Item 组件会携带一个 item prop,该组件会使用跟这个对象渲染 HTML 标签,对于快照测试而言,你需要用真实数据创建 item prop,这么做的目的是让测试更加值得信赖,因为它的输出更接近于生产环境中的输出内容。
快照测试的一个准则是快照测试必须是可确定的。换句话说,如果生成输出的代码没有改变,那么输出应该总是相同的,不管启动多少次测试都应该是这样,不过当你使用了会输出不确定结果的方式时,就产生了一个问题,比如 Date.now
。在你第一次运行之后,渲染出来的是 3minutes ago
,你第二天再次运行的时候,渲染出来的 1day ago
了。想要避免这种问题,你就需要模拟 Date.now
方法,让它总是返回相同的时间。以此使你的快照测试具有确定性。
test('renders correctly', () => {
const dateNow = jest.spyOn(Date, 'now');
const dateNowTime = new Date('2018');
dateNow.mockImplementation(() => dateNowTime);
const item = {
by: 'eddyerburgh',
id: 11122233,
score: 10,
time: dateNowTime - (1000 * 600),
title: 'vue-test-utils is released',
type: 'story',
url: 'https://vue-test-utils.vuejs.org/'
}
const wrapper = createWrapper({
propsData: {
item
}
})
dateNow.mockRestore();
expect(wrapper.element).toMatchSnapshot();
})
编写快照测试来捕获条件分支下的内容。
假设 Item 的部分实现为:
<span v-if="item.type !== 'job'" class="by">
by <router-link :to="'/user/' + item.by">{{ item.by }}</router-link>
</span>
<span v-if="item.type !== 'job'" class="comments-link">
| <router-link :to="'/item/' + item.id">{{ item.descendants }} comments</router-link>
</span>
<span>
{{ item.time | timeAgo }} ago
</span>
编写多种情况下的快照测试,上面的快照包含的 type 为 story,这个快照把 type 设置为 job,使用不同的分支内的内容进行渲染,生成多种情况下的快照。
test('renders correctly when item has no url', () => {
// ... 省略部分与上面代码相同
const item = {
by: 'eddyerburgh',
id: 11122233,
score: 10,
time: dateNowTime - (1000 * 600),
title: 'vue-test-utils is released',
type: 'job'
}
// ... 省略部分与上面代码相同
expect(wrapper.element).toMatchSnapshot()
})
理想情况下,组件输出的所有分支都应该被快照测试覆盖到,但这并不总是可能的,也并不可取。一个组件的大量快照测试意味着每次更改该组件时,都会有大量失败的快照测试。如果有太多失败的快照测试,那么更新所有失败的测试可能会变得非常困难,并且可能会意外的保存一个错误的快照,一般来说,不建议对一个组件编写超过三个快照测试。
单元测试对于测试组件中的逻辑是大有帮助的,然而对测试静态组件就显得力不从心,幸运的是,快照测试非常适用于测试静态组件的输出。
这时候才反过头去看按照 TDD 编写 Vue 组件的顺序 [跳转],你就知道在什么开始编写你的快照测试了。