Skip to content

Instantly share code, notes, and snippets.

@ben-doyle
Created September 16, 2019 00:36
Show Gist options
  • Save ben-doyle/505384e93f8c182a88408c43f4c8afbe to your computer and use it in GitHub Desktop.
Save ben-doyle/505384e93f8c182a88408c43f4c8afbe to your computer and use it in GitHub Desktop.
Testing react components

Testing react components

Guiding Principle

React testing library follows a guiding principle which is quoted below:

The more your tests resemble the way your software is used, the more confidence they can give you.

It's a little vague, and might be interpreted differently but to reword it for us, possibly this would be fitting:

The more your tests resemble the customer's needs and wants, the more confident you become in your application's implementation.

Current tech stack.

The reasons for this way of testing.

  • Stay as close to production testing: Use react, and not a third party tool to render the DOM before it is tested on.
  • Search a rendered DOM search via getByText and queryByText in a way that best suits it's implementation, and is also easy to spot when changes have been made.
  • Manageable single use case testing: Break down the tests into as smallest and most manageable single responsibility cases as possible.

Example tests

describe('HotelDetails', () => {
  it('should render the hotel name', () => {
    const { queryByText } = render(<HotelDetails {...defaultProps} />);
    const hotelName = queryByText('Marriot Hotel');
    expect(hotelName).toBeInTheDocument();
  });

  it('should render the hotel name but not the message button there is no messageHTML', () => {
    const { queryByText } = render(<HotelDetails {...defaultProps} messageHTML={undefined} />);
    const hotelName = queryByText('Marriot Hotel');
    const messageHotel = queryByText('Message hotel');
    expect(hotelName).toBeInTheDocument();
    expect(messageHotel).not.toBeInTheDocument();
  });
});

render(<HotelDetails {...defaultProps} />)

Render (using react) the full component. From here we can query parts of the rendered object and see if they are present.

render(<HotelDetails {...defaultProps} messageHTML={undefined} />);

In this example, we can render (using react) the full object with one of the prop inputs as undefined to see if the rendered object performs in the correct way.

When to use getByText or queryByText

getByText

getby* queries return the first matching node for a query, and throw an error if no elements match or if more than one match is found (use getAllBy instead).

  • If you need to test a returned object, use getByText
  • In the example below you can see that a click event needs to happen, therefore getByText must be used to get the object.

example test

it('should render listing of important price details and breakdown', () => {
  const { queryByText, getByText } = render(
    <Provider {...mockStores}>
      <Pricing {...defaultProps} />
    </Provider>
  );

  const additionalPricingInfo = getByText('Additional pricing information');
  fireEvent.click(additionalPricingInfo);

  const additionalFeesContent = queryByText(defaultProps.additionalInfoContent);
});

queryByText

queryBy* queries return the first matching node for a query, and return null if no elements match. This is useful for asserting an element that is not present. This throws if more than one match is found (use queryAllBy instead).

  • if you’re checking to see if something exists use queryByText.
  • In the example below you can see that a no action needs to be performed on the variable seatAssigned so queryByText is the best method.

example test

it('should render economy class with no seats selected', () => {
  const { queryByText } = render(<AssignedSeats seatInfo={seatInfo} cabinClass={cabinClass} />);

  const seatAssigned = queryByText('No seats selected');
  const cabin = queryByText('Economy / Coach (Y)');

  expect(seatAssigned).toBeInTheDocument();
  expect(cabin).toBeInTheDocument();
});

Why we agreed to stop/minimise snapshot testing.

From the above documenation:

Shallow rendering is useful to constrain yourself to testing a component as a unit, and to ensure that your tests aren't indirectly asserting on behavior of child components.

The biggest case against snapshot testing is that reading the changes on a failed snapshot is much harder to read and harder to debug.

From reference 2:

It’s often hard to see what changed by looking at the snapshot failure diff: did the snapshot fail because of your intended changes or because you’ve introduced a bug?

On a big project a simple change can lead to failures in dozens of snapshots in different parts of the codebase. Understanding each failure takes a lot of time, so often developers just update snapshots without looking at them at all, especially if it’s not their code.

Snapshot testing provides a false sense of security, something that has been very present in our pwa. It has lead to an overinflated test coverage percentage.

References and examples:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment