Testing a conditionally rendered section of a React Functional Component (Jest + @testing-library/react)

43 Views Asked by At

This is about testing a component which has a state update within a useEffect(). The issue is that the component is always tested against its default state.

There is a bunch of problems that goes similar to this but I haven't found any successful way of testing the following.

MyComponent.tsx

import React from 'react'

const MyComponent = () => {
  const [loaded, setLoaded] = React.useState(false)

  React.useEffect(() => {
    setLoaded(true)
  }, [])

  return (
    <div>
      {loaded ? (
        <div data-testid="true-text"> -> line 13
          true
        </div> -> line 15
      ) : (
        <div data-testid="false-text">
          false
        </div>
      )}

    </div>
  )
}

export default MyComponent

MyComponent.test.tsx

/** @jest-environment jsdom */

import React from "react";
import MyComponent from "../awesome-component";
import { render, waitFor } from "@testing-library/react";

describe('MyComponent', () => {
  test('should match snapshot', async () => {
    const setState = jest.fn()
    jest.spyOn(React, 'useState').mockImplementation(() => [false, setState]);

    const { asFragment, queryByTestId } = render(<MyComponent />);

    expect(asFragment()).toMatchSnapshot();

    await waitFor(() => {
      expect(setState).toHaveBeenNthCalledWith(1, true)
      expect(queryByTestId('false-text')).toBeTruthy()
    });
  });
});

Test result

 › 1 snapshot updated.
-----------------------|---------|----------|---------|---------|-------------------
File                   | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------------------|---------|----------|---------|---------|-------------------
All files              |   88.46 |    66.66 |     100 |   88.46 |                   
 awesome-component.tsx |   88.46 |    66.66 |     100 |   88.46 | 13-15             
-----------------------|---------|----------|---------|---------|-------------------
Snapshot Summary
 › 1 snapshot updated from 1 test suite.

I need to test the component rendered when loaded === true.

From the statement expect(setState).toHaveBeenNthCalledWith(1, true) I can see that the state update has happened but still expect(queryByTestId('false-text')).toBeTruthy() statement appears to be expecting correctly which means the template is rendered according to the false condition.

And in the coverage we can see that the loaded === true never rendered.

Can someone help me to evolve the same test to test after setLoaded(true). Thanks in advance.

1

There are 1 best solutions below

0
buddhiv On

It is not a good practice to mock the state of a react component. As per @jonsharpe's answer and my findings with the same idea, we should not try to change the behaviour of something that we do not own. This is different from mocking our own functions as we do know the behaviour or what we have written.

Instead, what we should do is trying to test the end states of components.