Skip to content Accessibility

Snapshot Resilience

Improve the integrity of component snapshot tests by naming mocked functions.

Published

Categories

Naming mocked functions in Jest improves the quality and resilience of snapshot testing.

In the following React component we want to test the contract between the callbacks passed as props and their corresponding click handlers are correct. Clicking the "Sign in" button should call the onSignIn prop, and clicking "Sign up" should call onSignUp:

Programming language (abbreviated): jsx

const WelcomeScreen = ({ onSignIn, onSignUp }) => (
<>
<button onClick={onSignIn}>Sign In</button>
<button onClick={onSignUp}>Sign Up</button>
</>
);

A simple snapshot captures this relationship between each button and its callback:

Programming language (abbreviated): js

it('should match the snapshot', () => {
const wrapper = shallow(<WelcomeScreen onSignIn={jest.fn()} onSignUp={jest.fn()} />);

expect(wrapper.getElement()).toMatchSnapshot();
});

/**
Produces:

exports[`should match the snapshot 1`] = `
<React.Fragment>
<button
onClick={[MockFunction]}
>
Sign In
</button>
<button
onClick={[MockFunction]}
>
Sign Up
</button>
</React.Fragment>`;
*/

If we introduce a bug, and intensionally break the contract between the "Sign In" button and its callback the unit test still passes:

Programming language (abbreviated): js

const WelcomeScreen = ({ onSignIn, onSignUp }) => (
<>
<button onClick={onSignUp}>Sign In</button> {/* 🔴 Wrongly calls onSignUp */}
<button onClick={onSignUp}>Sign Up</button>
</>
);

/**
Produces:

PASS src/WelcomeScreen.spec.js
✅ should match the snapshot
*/

The assertion passes because clicking "Sign In" still calls a function.

We know the wrong function will be called however our test doesn't.

This is where naming mock functions can help. We can make the snapshot aware of what function will be called using mockFn.mockName(value):

Programming language (abbreviated): js

it('should match the snapshot', () => {
const wrapper = shallow(
<WelcomeScreen
onSignIn={jest.fn().mockName('onSignInMock')}
onSignUp={jest.fn().mockName('onSignUpMock')}
/>
);

expect(wrapper.getElement()).toMatchSnapshot();
});

/**
Produces:

exports[`should match the snapshot 1`] = `
<React.Fragment>
<button
onClick={[MockFunction onSignInMock]}
>
Sign In
</button>
<button
onClick={[MockFunction onSignUpMock]}
>
Sign Up
</button>
</React.Fragment>`;
*/

Now when the same bug is introduced the unit test fails:

Programming language (abbreviated): js

const WelcomeScreen = ({ onSignIn, onSignUp }) => (
<>
<button onClick={onSignUp}>Sign In</button> {/* 🔴 Wrongly calls onSignUp */}
<button onClick={onSignUp}>Sign Up</button>
</>
);

/**
Produces:

FAIL src/WelcomeScreen.spec.js
❌ should match the snapshot

● should match the snapshot

expect(received).toMatchSnapshot()

Snapshot name: `should match the snapshot 1`

- Snapshot
+ Received

@@ -1,8 +1,8 @@
<React.Fragment>
<button
- onClick={[MockFunction onSignInMock]}
+ onClick={[MockFunction onSignUpMock]}
>
Sign In
</button>
<button
onClick={[MockFunction onSignUpMock]}
*/

Happy snapping!

About the author

I'm Callum, a Front-end Engineer at Nutmeg. Previously I wrote code for KAYAK, American Express, and Dell. Out of hours I publish blog posts (like this one) and tweet cherry-picks.

Feel free to follow or message me at @_callumhart on Twitter.