Jest Snapshot Testing: Verifying UI Component Stability
Snapshot testing is a powerful technique in Jest for verifying that a UI component's output has not changed unexpectedly. This challenge will guide you through implementing snapshot tests for a simple React component, ensuring its visual representation remains consistent over time.
Problem Description
Your task is to create Jest snapshot tests for a given React component. Snapshot tests work by taking a "snapshot" of the rendered output of a component and then comparing it against a previously saved snapshot. If the component's output changes, the test will fail, alerting you to potential regressions or unintended modifications.
Key Requirements:
- Component Rendering: You'll be provided with a basic React functional component.
- Jest Setup: Ensure you have Jest and
ts-jestconfigured for a TypeScript project. - Snapshot Creation: Write a test that renders the component and creates an initial snapshot.
- Snapshot Assertion: Use Jest's
toMatchSnapshot()matcher to assert that the rendered output matches the saved snapshot. - Handling Updates: Understand how to update snapshots when intended changes are made to the component.
Expected Behavior:
- The first time the test runs, a snapshot file (
.snap) will be generated. - Subsequent runs will compare the current rendered output against this snapshot.
- If the rendered output differs from the snapshot, the test will fail.
- You will be able to update the snapshot if the changes are intentional.
Examples
Let's consider a simple Button component.
Example 1: Basic Button
Input:
// src/components/Button.tsx
import React from 'react';
interface ButtonProps {
label: string;
onClick: () => void;
disabled?: boolean;
}
const Button: React.FC<ButtonProps> = ({ label, onClick, disabled = false }) => {
return (
<button onClick={onClick} disabled={disabled} className="btn">
{label}
</button>
);
};
export default Button;
Test (initially):
// src/components/Button.test.ts
import React from 'react';
import { render } from '@testing-library/react';
import Button from './Button';
describe('Button', () => {
it('should render correctly and match snapshot', () => {
const mockOnClick = jest.fn();
const { container } = render(<Button label="Click Me" onClick={mockOnClick} />);
expect(container).toMatchSnapshot();
});
});
Output (after first run):
A file named Button.test.ts.snap will be generated containing something like:
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Button should render correctly and match snapshot 1`] = `
<button class="btn">
Click Me
</button>
`;
Example 2: Disabled Button
Input:
// src/components/Button.tsx (same as above)
Test:
// src/components/Button.test.ts
import React from 'react';
import { render } from '@testing-library/react';
import Button from './Button';
describe('Button', () => {
it('should render a disabled button and match snapshot', () => {
const mockOnClick = jest.fn();
const { container } = render(<Button label="Disabled" onClick={mockOnClick} disabled />);
expect(container).toMatchSnapshot();
});
});
Output (after first run for this test):
A new snapshot entry will be added to Button.test.ts.snap:
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Button should render a disabled button and match snapshot 1`] = `
<button class="btn" disabled="">
Disabled
</button>
`;
Example 3: Button with different label
Input:
// src/components/Button.tsx (same as above)
Test:
// src/components/Button.test.ts
import React from 'react';
import { render } from '@testing-library/react';
import Button from './Button';
describe('Button', () => {
it('should render with a different label and match snapshot', () => {
const mockOnClick = jest.fn();
const { container } = render(<Button label="Submit" onClick={mockOnClick} />);
expect(container).toMatchSnapshot();
});
});
Output (after first run for this test):
Another snapshot entry in Button.test.ts.snap:
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Button should render with a different label and match snapshot 1`] = `
<button class="btn">
Submit
</button>
`;
If you were to change the Button component's implementation (e.g., add an id attribute) and rerun the tests without updating snapshots, the tests for all rendered snapshots would fail.
Constraints
- Your solution should be written in TypeScript.
- You are expected to use
@testing-library/reactfor rendering React components in your tests. - The provided component and tests should be runnable within a standard Jest testing environment for a React/TypeScript project.
Notes
- You will need to have
jest,@types/jest,react,react-dom,@testing-library/react, andts-jestinstalled and configured in your project. - The
toMatchSnapshot()matcher automatically handles the creation and comparison of snapshots. - When a snapshot test fails, review the diff to understand the changes. If the changes are intentional, run Jest with the
-uflag (e.g.,npm test -- -uoryarn test -u) to update the snapshot file. - Consider how passing different props to the component will affect the snapshot.
- The generated snapshot files should be placed alongside their corresponding test files.