在 React 应用开发中,确保组件的渲染输出正确是一项重要的测试任务。快照测试是一种有效的方法,可以帮助开发者捕捉并验证组件的渲染输出,确保其在不同的情况下保持一致。
什么是快照测试?
快照测试的基本思想是:
- 首次运行测试:将组件的渲染输出记录到一个快照文件中。
- 后续运行测试:将组件的渲染输出与快照文件进行对比,确保两者一致。
快照测试可以有效地捕获意外的更改,确保组件的渲染输出不会因为代码的改动而发生不必要的变化。
快速上手
假设我们有一个简单的组件 App
,它显示一个待办事项列表,并允许用户添加新的事项:
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"><inputtype="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();
});
首先从 render 方法中解构出 baseElement(注意 render 方法来源于 testing library)
接下来调用了 toMatchSnapshot(这个方法是 jest 提供的方法)来生成快照。
通过执行结果也可以看到,生成了一张快照,并且在我们的项目目录中(和你的测试文件是同级的),生成了一个名为 _snapshots_ 的目录,里面就是一张测试快照,测试快照的本质就是渲染出来的 DOM 的结构的字符串序列。
// Jest Snapshot v1, https://goo.gl/fbAQLPexports[`renders learn react link 1`] = `
<body><div><divclass="App"><inputtype="text"value=""/><button>添加</button><ul><li>苹果</li><li>香蕉</li><li>西瓜</li></ul></div></div>
</body>
`;
之后在下一次测试的时候,针对这个组件测试,就会将组件渲染出来的 DOM 结构的序列和之前的快照进行一个比对,看是否一致,如果和之前的快照是一致的,那么测试就通过,如果不一致(这一次渲染新增了DOM节点或者少了DOM 节点),那么就说明这一次渲染和之前的渲染不一致的,测试不通过。
如下图所示:
快照测试的注意事项
- 快照本身并不验证渲染逻辑是否正确:快照测试的主要目的是防止意外的更改。如果测试失败,需要检查是否有不期望的更改。
- 更新快照:如果确定渲染逻辑没有问题,但结构确实需要更改,可以使用
jest --updateSnapshot
命令更新快照。
避免大快照
在复杂的组件中,直接对整个组件进行快照会导致快照文件非常大。为了生成更小的快照,可以只针对特定的部分进行快照:
import { render, screen } from '@testing-library/react';
import App from './App';test('renders learn react link', () => {render(<App />);const content = screen.getByTestId('list');expect(content).toMatchSnapshot();
});
在这个例子中,我们只针对 TestUI
组件生成快照。
扩展场景
- 快照测试不仅适用于组件的 UI 测试,还可以用于其他场景。例如,对于 HTTP 请求的返回结果进行快照测试:
// 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();});
});
- 很多人喜欢把快照测试等同于组件的 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();})
});
总结
快照测试是一种简单有效的工具,可以帮助开发者确保组件的渲染输出在不同情况下保持一致。通过合理使用快照测试,可以提高代码的可靠性和可维护性。需要注意的是:
- 生成小快照:只取重要的部分来生成快照,确保快照是可读的。
- 合理使用快照:快照测试不仅限于组件测试,可以应用于任何可序列化的内容。
通过遵循这些最佳实践,可以避免快照测试中的“假错误”,确保测试的有效性。