6

Using Jest Mocks to Prevent Non-Deterministic or Otherwise Changing Components f...

 3 years ago
source link: https://typeofnan.dev/using-jest-mocks-to-prevent-nondeterministic-changing-snapshot-diffs/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
failing tests

Snapshot testing is great. I work with React a lot and frequently use Storybook to do visual and snapshot testing of my components. It can help quickly identify any regressions and undesired behavior by pointing out when your components render differently than they did the last time you ran your test suite.

The Problem of Non-Deterministic or Otherwise-Changing Components

Snapshot diffs can sometimes be meaningless. For example, if you snapshot test a component that contains a random number generator, you’ll constantly get snapshot diffs when nothing has really changed. These diffs are essentially a nuisance—we don’t want to keep having to approve them over and over.

A Concrete Example: The Current Timestamp

Let’s say our app has a section that tells users the current time. My app will be an extreme example and will tell the time down to the second. Note that this is a functional React component with hooks. If you don’t understand the code, no worries, I’ll show you what gets rendered in the browser in a moment.

timer.jsx

import React, { useState, useEffect } from 'react';

const getCurrentTimestamp = () => new Date().toLocaleTimeString();

export const Timer = () => {
  const [time, setTime] = useState(getCurrentTimestamp());
  useEffect(() => {
    let refresh = setInterval(() => {
      setTime(getCurrentTimestamp());
    }, 1000);
    return () => {
      clearInterval(refresh);
    };
  }, []);

  return <div>The time is {time}</div>;
};

If I fire up my app and load it in the browser, I see a very simple dynamic clock.

simple clock in browser

Snapshot Testing our Clock

Here’s a really simply Storybook test for my Timer component.

import React from 'react';
import { Timer } from '../timer';

export default {
  title: 'Components/Timer',
  component: Timer,
};

export const Default = () => <Timer />;

However, whenever I run snapshot testing for this component, it fails!

failed test

The time changes every time I take a snapshot of the component, so of course the snapshot will fail: we get something different rendering every time.

Fixing the Issue: Mocking Out Our Dynamic Component

Fortunately, there’s a pretty simple fix for our issue! In this instance, our Storybook-related snapshots are driven by a config file, storybook.test.jsx. Prior to any modification, this config file looks like this:

import initStoryshots from '@storybook/addon-storyshots';
initStoryshots();

We can modify this file by mocking the import of Timer from the timer.jsx file! That way, any use of the component in our snapshot tests will use the mock we provide here rather than the actual component.

import React from 'react';
import initStoryshots from '@storybook/addon-storyshots';

jest.mock('../timer.tsx', () => ({
  Timer: () => <timer />,
}));

initStoryshots();

Here we’ve basically told jest that, instead of rendering our dynamic component for snapshot tests, just go ahead and render <timer />. That way, we won’t be bothered by dynamic snapshots but we’ll also be assured that the timer is rendering when it should.

We can now run our snapshot tests and will no longer be annoyed by these frequently-changing components!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK