Skip to content

Instantly share code, notes, and snippets.

@karolk
Last active July 27, 2017 14:12
Show Gist options
  • Save karolk/ec2adb0d19265bd2950d38272087e415 to your computer and use it in GitHub Desktop.
Save karolk/ec2adb0d19265bd2950d38272087e415 to your computer and use it in GitHub Desktop.
Snapshot testing

Snapshot testing

Snapshot tests are a very useful tool whenever you want to make sure your UI does not change unexpectedly.

Instead of rendering the graphical UI, which would require building the entire app, you can use a test renderer to quickly generate a serializable value for your React tree.

Oftentimes when we not using snapshot testing you would end up writing tests like this:

// BuyNow.js
const BuyNow = ({ price, text }) => (<div>
  <span className="price">£{price}</span>
  <button className="btn-primary">{text}</button>
</div>);

// /__test__/BuyNow.js
import { shallow } from 'enzyme';
import BuyNow from '../BuyNow';

describe('The BuyNow component', () => {
  it('renders with the right price', () => {    
    const props = { price: 25 };
    const tree = shallow(<BuyNow {...props} />);

    expect(tree.find('span').length()).toEqual(1);
    expect(tree.find('span').text()).toEqual('£25');
    expect(tree.find('span').hasClass('price')).toBe(true);
  });
});

This is a very tedious way of checking tag-by-tag if the component renders correctly.

First encounter

The workflow with snapshot is much simpler, the testing looks as follows:

// __test__/BuyNow.js
import { shallow } from 'enzyme';  
import toJson from 'enzyme-to-json';  
import BuyNow from '../BuyNow';

describe('The BuyNow component', () => {  
  it('renders the right price and button', () => {    
    const props = { price: 25, text: 'Buy now' };
    const tree = shallow(<BuyNow {...props} />);

    expect(toJson(tree)).toMatchSnapshot();
  });
});

After running this test for the 1st time it will generate a new directory __snapshots__ and the file inside will look like this:

exports[`The BuyNow component renders the right price and button 1`] = `
<div>
  <span
    className="price">
    £
    25
  </span>
  <button
    className="btn-primary">
    Buy now
  </button>
</div>
`;

Jest will also inform that there was 1 new snapshot written. Your job is to evaluate this snapshot's tree and decide if it's going to render correctly.

If the component has many props or renders in a conditional fashion then it is necessary for good coverage to test all important permutations of props and state. Jest will then create multiple snapshots, 1 per an it block.

Snapshots should be checked into the repository as other tests.

Lifecycle

At some point in the following weeks jest will warn that a snapshot has been updated. This will happen if you change the test's description or if the rendered tree changes. Again it is your job to evaluate if the change came about because you wrote some code or it was an unwanted side-effect of some other code change (we still get benefits of TDD with snapshots).

If the change is good you can just accept snapshot by running npm test -- -u or yarn test -- -u. Jest will update the snapshot and the component is tested.

Troubleshooting

My diffs are so long, I don't have time to look through so much code.

Break down your components. You shouldn't have large components because the near-infinite amount of permutations of state and props will make it impossible to test. Remember components are just functions, you wouldn't normally have a function with 7 parameters?

My snapshots are incorrect, I am using component <Foo />, but in snapshots jest writes <Component /> and my tests don't pass.

This is a known problem of older istanbul, happening mostly when tests are run with --coverage flag (which would often happen on CI but not locally). This is reported as fixed so make sure you are using latest version of tools. If the problem persists you can get around that by specifying a property displayName on your React components.

Read more: http://blog.ricca509.me/snapshot-testing-and-why-it-makes-sense/ (some examples were borrowed from this article).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment