Jest快照测试的详细教程

371 阅读3分钟

快照测试是编写轻量级组件测试的一种常见方式。当一个快照测试第一次运行时,它将其输出(例如,渲染组件的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>
`;

同样,这也是测试改变/重修组件的最基本方法。然而,这种最基本的方法有两个缺点,从前面快照的输出中可以看出:

    1. 整个组件被再次快照。(冗余)
    1. 不清楚快照的执行是为了断言关于重新渲染的组件的变化。相反,它只是一个直接的再次快照。(缺少背景)。

让我们为快照测试实现一个更好的版本,以断言由用户交互或其他副作用引起的重新渲染后可能发生的差异。首先,安装这个整洁的辅助库来断言快照的差异。

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快照测试