Skip to main content

测试快照

在对组件进行测试的时候,往往需要从两个方面进行测试:

  1. 交互:确保组件在进行交互时功能正常。
  2. 渲染:确保组件渲染输出正确(比如不会多一个或者少一个 DOM 元素)。

针对渲染方面的测试,我们就可以使用快照来进行测试。所谓快照,就是给渲染出来的DOM元素拍一张“照片”(将最终渲染出来的 =DOM以字符串序列的方式记录下来)。

案例

比如有如下组件:

import { useState } from "react";

function App() {
const [items, setItems] = useState(["苹果", "香蕉", "西瓜"]);
const [value, setValue] = useState("");
const lis = items.map((it, idx) => <li key={idx}>{it}</li>);

function addItem() {
if (items) {
const newItems = [...items];
newItems.push(value);
setItems(newItems);
setValue("");
}
}
return (
<div className="App">
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
<button onClick={addItem}>添加</button>
<ul>{lis}</ul>
</div>
);
}

export default App;

针对此组件,测试增加测试快照:

import { render } from '@testing-library/react';
import App from './App';

test('renders learn react link', () => {
const { baseElement } = render(<App />);

// 生成测试快照
expect(baseElement).toMatchSnapshot();
});

通过执行结果可以看到,生成了一张快照,并且在项目目录中(与测试文件是同级的),生成了一个名为_snapshots_的目录,里面就是一张测试快照。

测试快照的本质就是渲染出来的DOM的结构的字符串序列:

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders learn react link 1`] = `
<body>
<div>
<div
class="App"
>
<input
type="text"
value=""
/>
<button>
添加
</button>
<ul>
<li>
苹果
</li>
<li>
香蕉
</li>
<li>
西瓜
</li>
</ul>
</div>
</div>
</body>
`;

之后在进行下一次测试的时候,针对这个组件测试,就会将组件渲染出来的DOM结构的序列和之前的快照进行一个比对,看是否一致:

  • 如果和之前的快照是一致的,那么测试就通过。
  • 如果不一致(这一次渲染新增了DOM节点或者少了DOM节点),那么就说明这一次渲染和之前的渲染不一致,则测试不通过。

注意点

  • 快照本身并不验证渲染逻辑是否正确,它只是防止意外更改,所以当测试快照不通过的时候,就需要检查一下所需的元素、样式是否发生了不期望的改变。
  • 快照失败的时候,如果确定渲染逻辑没有问题,确确实实是结构需要发生更改,那么可以更新快照,可通过jest --updateSnapshot这个命令进行更新。

避免大快照

在真实的项目中,业务组件会往往比较复杂,一个大组件里面会嵌套很多的小组件,这个时候如果直接对整个大组件进行快照,那么就会导致快照文件无比的巨大,因为快照会将嵌套的组件的DOM结构也记录下来。

这种情况下,可指定只生成某一个部分的快照,这种快照称之为小快照。

import { render, screen } from '@testing-library/react';
import App from './App';

test('renders learn react link', () => {
render(<App />);

// 生成testid为list的快照
const content = screen.getByTestId('list');
expect(content).toMatchSnapshot();
});

扩展场景

很多开发者喜欢把快照测试等同于组件的UI测试,但是快照有些时候在其他的某一些场景下使用也非常方便:

如下示例:

// getUserById.ts
const getUserById = async (id: string) => {
return request.get('user', {
params: { id }
})
}

// getUserById.test.ts
describe('getUserById', () => {
it('可以获取 userId == 1 的用户', async () => {
const result = await getUserById('1')
expect(result).toEqual({
// 非常巨大的一个 JSON 返回...
})
})
});

比如在上面的示例中,http请求返回的结果是比较大的,这个时候就会有一些冗余的代码,在expect断言的时候就会比较麻烦。此时就可以使用快照:

// getUserById.ts
const getUserById = async (id: string) => {
return request.get('user', {
params: { id }
})
}

// getUserById.test.ts
describe('getUserById', () => {
it('可以获取 userId == 1 的用户', async () => {
const result = await getUserById('1')
expect(result).toMatchSnapshot();
})
});