Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jest snapshots testing

Tags:

jestjs

I use jest snapshot testing for one of my component and the generated snap file is huge (199Kb and 4310 lines). All snapshot file is print to the console (that's 3-4 secs of rendering) when the snapshot test fails and it gave me this "you're doing something wrong" feeling.

So my question is : Am i using snapshot testing correctly ?

component code :

import _ = require('lodash');
import React = require('react');
import {TranslatedMessage} from 'translator';

import {UserProfile} from './user-profile';
import {ICustomerProfile} from '../customer/customer-profile';

interface IUserProfile {
    firstName: string;
    lastName: string;
    id: string;
    customer: ICustomerProfile;
    job: string;
    email: string;
    contacts: string;
    phoneNumber: string;
}

interface IUserProfileProps {
    contact: IUserProfile;
}

interface IUserProfileState {}

export class UserProfile extends React.Component<IUserProfileProps, IUserProfileState> {
    constructor(props: IUserProfileProps) {
        super(props);
    }

    public render(): JSX.Element {
        return (
            <div className="ext-admin-user-infos-details">
                <div className="ext-admin-user-infos-details-content">
                    <div className="row">
                        <div className="col-md-12">
                            <h3>{this.props.contact.firstName } {this.props.contact.lastName}</h3>
                            <p className="ext-subtitle">
                                <span className="ext-minor">{this.props.contact.id}</span>
                            </p>
                        </div>
                    </div>
                    <div className="row">
                        <div className="col-md-8">
                            <div className="ext-admin-user-infos-card">
                                <h6>
                                    <TranslatedMessage messageKey="common.labels.customer" />
                                </h6>
                                <ul>
                                    <li>{this.props.contact.customer.name}</li>
                                </ul>
                            </div>
                            <div className="ext-admin-user-infos-card">
                                <h6>
                                    <TranslatedMessage messageKey="admin.contact.infos.job" />
                                </h6>
                                <ul>
                                    <li>{this.props.contact.job}</li>
                                </ul>
                            </div>
                            <div className="ext-admin-user-infos-card">
                                <h6>
                                    <TranslatedMessage messageKey="admin.contact.infos.email" />
                                </h6>
                                <ul>
                                    <li>{this.props.contact.email}</li>
                                </ul>
                            </div>
                        </div>
                        <div className="col-md-4">
                            <div className="ext-admin-user-infos-card">
                                <h6>
                                    <TranslatedMessage messageKey="common.labels.followed" />
                                </h6>
                                <ol>
                                    {this.renderContacts(this.props.contact.contacts)}
                                </ol>
                            </div>
                            <div className="ext-admin-user-infos-card">
                                <h6>
                                    <TranslatedMessage messageKey="common.labels.phone" />
                                </h6>
                                <ul>
                                    <li>{this.props.contact.phoneNumber}</li>
                                </ul>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        );
    }

    protected renderContacts(contacts: IUserProfile[]): JSX.Element[] {
        let contacts= [];
        if (sales) {
            _.map(sales, function(contact: IUserProfile): void {
                salesContact.push(
                    <li>
                        { contact.firstName}
                        { contact.lastName}
                    </li>
                );
            });
        }

        return contacts;
    }
}

And the test file

jest.mock('TranslatedMessage');

import React = require('react');
import {render} from 'enzyme';

import {user} from '../../../tests/tools';

import {UserProfile} from '../../../app/components/user-profile/user-profile';

describe('UserProfile', () => {
    it('should match the snapshot', () => {
        const tree = render(<UserProfile user={user} />);

        expect(tree).toMatchSnapshot();
    });
});
like image 766
sanghin Avatar asked Feb 21 '26 20:02

sanghin


1 Answers

Trust that feeling.

You're using snapshot testing correctly, but you've reached the point where you need to break down large components into smaller components. Breaking them apart will allow you to mock the children components, which will cut down on your snapshot size (per snapshot, not in aggregate) and make your diffs easier to see and fix.

For example, instead of:

export class UserProfile extends Component {
  public render() {
    return (
      <div className="ext-admin-user-infos-details">
        <div className="ext-admin-user-infos-details-content">
          <div className="row">
            <div className="col-md-12">
              <h3>{this.props.contact.firstName } {this.props.contact.lastName}</h3>
              <p className="ext-subtitle">
                <span className="ext-minor">{this.props.contact.id}</span>
              </p>
            </div>
          </div>
          // ...
        </div>
      </div>
    )
  }
}

You do:

export class UserProfile extends Component {
  public render() {
    return (
      <div className="ext-admin-user-infos-details">
        <div className="ext-admin-user-infos-details-content">
          <div className="row">
            <div className="col-md-12">
              <UserProfileName
                first={this.props.contact.firstName}
                last={this.props.contact.firstName}
                contactId={this.props.contact.id}
              />
            </div>
          </div>
          // ...
        </div>
      </div>
    )
  }
}

export class UserProfileName extends Component {
  public render() {
    return (
      <div>
        <h3>{this.props.contact.first} {this.props.contact.last}</h3>
        <p className="ext-subtitle">
          <span className="ext-minor">{this.props.contact.contactId}</span>
        </p>
      </div>
    );
  }
}

Notice that I've moved the logic for rendering the user's name to another component. Here's the updated test, mocking the child component:

jest.mock('TranslatedMessage');
jest.mock('UserProfileName'); // Mock this and other children

import React = require('react');
import {render} from 'enzyme';

import {user} from '../../../tests/tools';

import {UserProfile} from '../../../app/components/user-profile/user-profile';

describe('UserProfile', () => {
  it('should match the snapshot', () => {
    const tree = render(<UserProfile user={user} />);

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

The snapshot for this will be much smaller than if it was all in one component. Of course you would have tests for the children components as well:

import React = require('react');
import {render} from 'enzyme';

import {UserProfileName} from '../../../app/components/user-profile/user-profile-name';

describe('UserProfileName', () => {
  it('should match the snapshot with all props', () => {
    const tree = render(<UserProfile first="Test" last="Testerson" contactId="test-id" />);

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

  it('should render without a first name', () => {
    const tree = render(<UserProfile last="Testerson" contactId="test-id" />);

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

  it('should render without a last name', () => {
    const tree = render(<UserProfile first="Test" contactId="test-id" />);

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

Notice that in these tests I added two more cases at the end. When you break components down like this, it's a lot easier to understand and test for specific use-cases of the children components!

Finally, an added benefit of this approach is that now you have a re-usable component that knows how to render a user name! You could generalize this and plop it in whenever you need it.

like image 141
Rick Hanlon II Avatar answered Feb 24 '26 04:02

Rick Hanlon II



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!