快照测试是编写轻量级组件测试的一种常见方式。当一个快照测试第一次运行时,它将其输出(例如,渲染组件的HTML结构)存储在一个快照输出文件中。每次快照测试再次运行时,都会创建另一个快照输出文件;该文件用于与旧的快照测试的输出文件进行比较。如果快照的输出有变化,开发者就接受或拒绝这些变化。这样一来,开发者就可以对他们最近的变化有一个概览。
import React from 'react';
const App = () => {
const [counter, setCounter] = React.useState(0);
return (
<div>
<h1>My Counter</h1>
<Counter counter={counter} />
<button type="button" onClick={() => setCounter(counter + 1)}>
Increment
</button>
<button type="button" onClick={() => setCounter(counter - 1)}>
Decrement
</button>
</div>
);
};
export const Counter = ({ counter }) => (
<div>
<p>{counter}</p>
</div>
);
export default App;
代码片段显示了一个React应用程序,它实现了一个计数器,可以通过使用两个渲染的按钮中的一个,用React Hook增加/减少。一个直接的React组件的快照测试可以通过以下方式实现。
import React from 'react';
import renderer from 'react-test-renderer';
import App from './App';
describe('App', () => {
it('renders', () => {
const component = renderer.create(<App />);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
});
如果运行快照测试,将生成以下快照输出文件。
exports[`App increments the counter 1`] = `
<div>
<h1>
My Counter
</h1>
<div>
<p>
0
</p>
</div>
<button
onClick={[Function]}
type="button"
>
Increment
</button>
<button
onClick={[Function]}
type="button"
>
Decrement
</button>
</div>
`;
这就是React中快照测试的最基本方法。本教程的问题是。如果你想对重新渲染的组件进行快照测试,会发生什么?
例如,在我们的React应用中,人们可以调用两个按钮中的一个来引起状态变化,从而增加计数器,这将导致组件的重新渲染。之后,可以使用一个新的快照测试来断言渲染输出的差异。
import React from 'react';
import renderer from 'react-test-renderer';
import App from './App';
describe('App', () => {
it('increments the counter', () => {
const component = renderer.create(<App />);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
component.root.findAllByType('button')[0].props.onClick();
tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
});
运行快照测试后,我们将在同一个快照输出文件中得到两个快照输出。下面的代码片断只显示了改变/重新渲染的组件的第二个输出:
exports[`App increments the counter 2`] = `
<div>
<h1>
My Counter
</h1>
<div>
<p>
1
</p>
</div>
<button
onClick={[Function]}
type="button"
>
Increment
</button>
<button
onClick={[Function]}
type="button"
>
Decrement
</button>
</div>
`;
同样,这也是测试改变/重修组件的最基本方法。然而,这种最基本的方法有两个缺点,从前面快照的输出中可以看出:
-
- 整个组件被再次快照。(冗余)
-
- 不清楚快照的执行是为了断言关于重新渲染的组件的变化。相反,它只是一个直接的再次快照。(缺少背景)。
让我们为快照测试实现一个更好的版本,以断言由用户交互或其他副作用引起的重新渲染后可能发生的差异。首先,安装这个整洁的辅助库来断言快照的差异。
npm install --save-dev snapshot-diff
第二,通过扩展你的Jest expect方法的新功能来设置这个辅助库:
import React from 'react';
import renderer from 'react-test-renderer';
import { toMatchDiffSnapshot } from 'snapshot-diff';
expect.extend({ toMatchDiffSnapshot });
import App from './App';
describe('App', () => {
it('increments the counter', () => {
...
});
});
第三,利用新的功能为两个组件渲染之间的差异创建一个快照:
import React from 'react';
import renderer from 'react-test-renderer';
import { toMatchDiffSnapshot } from 'snapshot-diff';
expect.extend({ toMatchDiffSnapshot });
import App from './App';
describe('App', () => {
it('increments the counter', () => {
const component = renderer.create(<App />);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();
component.root.findAllByType('button')[0].props.onClick();
const treeUpdate = component.toJSON();
expect(tree).toMatchDiffSnapshot(treeUpdate);
});
});
现在,你在快照输出文件中得到了重新渲染的组件的第二个输出:
exports[`App increments the counter 2`] = `
"Snapshot Diff:
- First value
+ Second value
@@ -2,11 +2,11 @@
<h1>
My Counter
</h1>
<div>
<p>
- 0
+ 1
</p>
</div>
<button
onClick={[Function onClick]}
type=\\"button\\""
`;
如果你把这个快照的输出与前一个快照进行比较,你可以看到我们摆脱了上述两个缺点。首先,我们没有再次渲染整个组件,而只是渲染了除了周围环境之外有变化的部分。第二,快照测试的输出看起来不再是渲染过的组件,而是用+和-前缀表示的两个输出之间的差异。只有通过查看快照的输出文件,开发者才能知道:1)快照测试是由组件的变化引起的;2)渲染的输出已经从X变成了Y。
继续阅读。如何浅层渲染Jest快照测试