Zain
Zain

Reputation: 61

Why is first Jest test causing second test to fail?

I have a React component which renders a list of components. I'm running some tests which mock the axios module which loads in the users from JSONPlaceHolder. All works fine including the the async test and it's mocks data as expected. However if you look at the code below it only passes as long as the first test is commented out? Am I missing something? Been banging my head for ages. Is there some cleanup that needs to be done between tests? Thanks in advance.

import { waitForElement } from 'enzyme-async-helpers';
import UsersList from '../UsersList';
import axios from 'axios';

const mockUsers = [
    {
        "id": 1,
        "name": "Leanne Mock",
        "username": "Bret",
        "email": "[email protected]"
    },
    {
        "id": 2,
        "name": "John Mock",
        "username": "Jospeh",
        "email": "[email protected]"
    }
]

axios.get.mockImplementationOnce(() => Promise.resolve({
    data: mockUsers
}))


describe('<UsersList /> tests:', () => {

        //WHEN I UNCOMMENT THIS TEST THE SECOND TEST FAILS?
        test('It renders without crashing', (done) => {
           // const wrapper = shallow(<UsersList />);
        });

        test('It renders out <User /> components after axios fetches users', async () => {
            const wrapper = shallow(<UsersList />);
            expect(wrapper.find('#loading').length).toBe(1); //loading div should be present


            await waitForElement(wrapper, 'User'); //When we have a User component found we know data has loaded
            expect(wrapper.find('#loading').length).toBe(0); //loading div should no longer be rendered
            expect(axios.get).toHaveBeenCalledTimes(1);

            expect(wrapper.state('users')).toEqual(mockUsers); //check the state is now equal to the mockUsers
            expect(wrapper.find('User').get(0).props.name).toBe(mockUsers[0].name); //check correct data is being sent down to User components
            expect(wrapper.find('User').get(1).props.name).toBe(mockUsers[1].name);
        })

})

The Error message I get is:

    The render tree at the time of timeout:
     <div
      id="loading"
    >
       Loading users
    </div>

  console.warn node_modules/enzyme-async-helpers/lib/wait.js:42
    As JSON:
     { node:
       { nodeType: 'host',
         type: 'div',
         props: { id: 'loading', children: ' Loading users ' },
         key: undefined,
         ref: null,
         instance: null,
         rendered: ' Loading users ' },
      type: 'div',
      props: { id: 'loading' },
      children: [ ' Loading users ' ],
      '$$typeof': Symbol(react.test.json) }

Test Suites: 1 failed, 1 total
Tests:       2 failed, 2 total

Upvotes: 6

Views: 5033

Answers (2)

Bus42
Bus42

Reputation: 152

Having the same issue, but I'm not making a request. I'm building a client-side React application and testing for the render of sub-components. I have an image carousel that loads on my Home component and I'm writing tests for it. If I comment out all but one test (any test) it passes. If I have more than one test (any combination of tests), it fails. I've tried async/await/waitFor, react-test-renderer, using done() - nothing seems to change this behavior.

import { render, screen } from '@testing-library/react';
import ImageCarousel from '../carousel/ImageCarousel';
import localPhotos from '../../data/localPhotos';

// passing in the full array of images is not necessary, it will cause the test to time out
const testArray = localPhotos.slice(0, 3);

describe('Image Carousel', () => {
    it('renders without error', () => {
      render(<ImageCarousel images={testArray} />);
      const imageCarousel = screen.getByTestId('image-carousel');
      expect(imageCarousel).toBeInTheDocument();
    });
    // it('displays the proper alt text for images', () => {
    //   render(<ImageCarousel images={testArray} />);
    //   const photo1 = screen.getByAltText(localPhotos[0].alt);
    //   const photo2 = screen.getByAltText(localPhotos[1].alt);
    //   expect(photo1.alt).toBe(localPhotos[0].alt);
    //   expect(photo2.alt).toBe(localPhotos[1].alt);
    // });
    // it("displays full-screen icons", () => {
    //   render(<ImageCarousel images={testArray} />);
    //   const fullScreenIcons = screen.getAllByTestId('full-screen-icon');
    //   expect(fullScreenIcons.length).toBe(testArray.length);
    // })
  // shows controls when showControls is true
  // does not show controls when showControls is false
  //   it('displays the proper number of images', () => {
  //     render(<ImageCarousel images={testArray} />);
  //     const carousel_images = screen.getAllByTestId('carousel_image');
  //     expect(carousel_images.length).toBe(testArray.length);
  //   });
  // calls next when clicked
  // calls previous when clicked
  // returns to first image when next is clicked and last image is shown
  // moves to last image when previous is clicked and first image is shown
});

Upvotes: 2

Doug
Doug

Reputation: 6497

You only mock the first axios.get call because you are using mockImplementationOnce.

When you shallow(<UsersList />) twice, the second time is timing out loading the users.

You can add a beforeEach method with a mockResolvedValueOnce inside, to mock the axios.get before every single test:

beforeEach(() => {
  axios.get.mockResolvedValueOnce({data: mockUsers});
}

Upvotes: 2

Related Questions