How to test the snapshot of UI with changing ids like chart or primary keys

Snapshot testing works by serializing the webpage and comparing it with the latest value. But when it comes to dynamically generated values, it will fail. Like if you have a list that contains the primary key as shown below.

When you store the snapshot for the first time, the keys will be 234, 456,789.

When you take the snapshot of it again, the keys are going to be different from the previous one.

How to solve the above problem?

There are many solutions that can be employed to solve the snapshot testing.

Mocking of non-deterministic data

If we have a navbar that displays the current date and time using API. So the snapshot testing of the navbar will always result in failure because the time is changing. In this case, we can use the mocking of Date.now() API so that the time is displayed the same during testing.

Mocking of Date.now() API using jest

Date.now = jest.fn(() => 1482363367071);

Lets consider the sample react component with Date function

import React, { Component } from "react";

class SnapShotTests extends Component {
  render() {
    return (
      <div className="App">
        {Date.now()}
      </div>
    );
  }
}

export default SnapShotTests;

The snapshot test for the above component will be

import React from "react";
import renderer from "react-test-renderer";
import SnapShotTests from "../SnapShotTests";

it("renders correctly", () => {
  Date.now = jest.fn(()=>1234)
  const tree = renderer.create(<SnapShotTests />).toJSON();
  expect(tree).toMatchSnapshot();
});

Passing ids as props

If you are having elements with dynamic ids or UUIDs, then it is going to fail the snapshot test every time as the generated id will be different. In that scenario, you can pass the ids as a prop to the component. You can also set default prop for that id so that you don't have to pass the prop.

We can modify the sample component above to accept the date as a prop.

import React, { Component } from "react";
import PropTypes from 'prop-types';

class SnapShotTests extends Component {
  render() {
    const { date } = this.props.
    return (
      <div className="App">
        {date}
      </div>
    );
  }
}

SnapShotTests.proptypes = {
  date: PropTypes.string
}

export default SnapShotTests;
import React from "react";
import renderer from "react-test-renderer";
import SnapShotTests from "../SnapShotTests";

it("renders correctly", () => {
  const tree = renderer.create(<SnapShotTests data={1234}/>).toJSON();
  expect(tree).toMatchSnapshot();
});

Using Property Matchers

Property matchers are useful when you want to match the snapshot with the object.

We can not always be able to mock the functions. When you are taking the snapshot of charts or visualization, where we have random ids for the elements. It is difficult to mock the inner functions of the visualization library.

In order to use snapshot testing for the above, we need to pass the arguments to toMatchSnapshot function, and update the snapshot files. Jest will update/create the snapshot files accordingly.

Let us consider a simple react component as shown below

import React, { Component } from "react";

class SnapShotTests extends Component {
  render() {
    return (
      <div className="App">
        {Date.now()}
      </div>
    );
  }
}

export default SnapShotTests;

Write the snapshot test for the above component bypassing the property matchers to the toMatchSnapshot file.

import React from "react";
import renderer from "react-test-renderer";
import SnapShotTests from "../SnapShotTests";

it("renders correctly", () => {
  const tree = renderer.create(<SnapShotTests />).toJSON();
  expect(tree).toMatchSnapshot({
  children: [ expect.any(String) ] });
});

The snapshot file will be generated as shown below.

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

exports[`renders correctly 1`] = `
Object {
  "children": Array [
    Any<String>,
  ],
  "props": Object {
    "className": "App",
  },
  "type": "div",
}
`;

As you can see, the Date.now() is now replaced with Any<String>. So the snapshot test will always pass the test without the need to mock the Date.now() function.