Penn Wang

Snapshot Test is God

记录一下 snapshot test 的使用心得

我的职业生涯初期几乎没有写过单元测试 。刚开始做 java,在一个国内银行的外包项目组,经理天天都在催进度,测试是不可能测试的。后来做前端,虽然摸索着写了点 nodejs 的测试,研究了 TDD 。但是前端页面是不让测试的,按照我们leader的说法,页面测什么测,不如直接上了,让用户帮我们测。 大概两三年前,进了现在的组,所有的 PR 都要 build pass,而 unit test 是 build pipeline 的重要一环,于是不得不学着写测试。只是这么一写就没法停下来了 -- 是公司没法停下来了,我肯定是不想写的,多一行代码都不想写(😂)。本来就写业务代码而已,现在好了,写了代码还要写测试,改了代码还要改测试。烦死了 。 然后就了解到了 snapshot 测试。

snapshot 测试是个啥

单元测试的核心思想是判定预期输出和实际输出是否相等。举个简单的例子:函数 sum(a, b) 的预期输出是 a + b,那么我们就写一个测试用例,输入 1 和 2,预期输出是 3,然后运行测试,如果实际输出也是 3,那么测试通过,否则测试失败。 snapshot 是一种特殊的测试方法,它会把函数的输出保存下来,作为一个 snapshot 文件。当我们第一次运行测试时,snapshot 文件会被创建,并且保存了函数的输出。以后每次运行测试时,snapshot 文件会被读取,并且和函数的实际输出进行比较。如果两者不相等,那么测试失败,否则测试通过。

为啥说它是个神

一般测试,输出(output)是需要自己管理的。比如 expect(sum(1, 2)).toBe(3),预期输出是 3,我们需要自己写代码来管理这个 output。

而 snapshot 测试,输出是由 snapshot 文件管理的,咱不需要自己哭哈哈的去改测试文件(当然snapshot有变更的情况下还是要人工看一眼的)。如果确定代码没问题,就用 update 指令更新 snapshot 文件就好了。这样就大大降低了测试的维护成本。

再一个,snapshot 测试用来测试组件外观的时候,比 tobeInTheDocument 这种断言效率更高。只需要一行代码 expect(container).toMatchSnapshot() 就可以了,不需要写一大堆断言来判断组件的各个部分是否存在,是否有正确的文本内容,是否有正确的样式等等。只要 snapshot 文件里保存了组件的外观,那么每次运行测试时就可以直接比较组件的外观和 snapshot 文件里的外观,如果两者不相等,那么测试失败,否则测试通过。

另外,虽然 snapshot 的使用依赖 react render,但 并不意味着 snapshot 只能用在 react 上。其实 snapshot 的核心思想是把输出保存下来,作为一个 snapshot 文件,这个思想是可以应用在任何语言和框架上的。只要我们能把函数的输出保存下来,就可以使用 snapshot 测试。比如很复杂的函数,输出是一个对象,我们可以把这个对象保存下来,作为一个 snapshot 文件。以后每次运行测试时,我们就可以把这个对象和 snapshot 文件进行比较,如果两者不相等,那么测试失败,否则测试通过。

在我个人负责的很多模块里,已经大量使用 snapshot 测试,不仅测试了组件的外观,还测试了函数的输出。感觉 snapshot 测试真的很神,降低了测试的维护成本,提高了测试的效率,让我这个不想写测试的人也能轻松地写测试了。

(完)

All rights reserved.