0

I have a simple component, it fetches async list of posts.

export const Posts = () => {
  const [list, dispatch] = useReducer(listReducer, []);

  useEffect(() => {
    fetchList(dispatch);
  }, []);

  return (
    <ul>
      {list.map((el) => (
        <li key={el.id}>{el.title}</li>
      ))}
    </ul>
  );
};

In other file i keep the logic:

export const fetchList = async (dispatch) => {
  try {
    const result = await api.get('/list/') /* AXIOS */
    dispatch({ type: LIST_SUCCES, payload: result.data.list })
  } catch (error) {
    dispatch({ type: LIST_FAILURE })
  }
}

export const listReducer = (state, action) => {
  switch (action.type) {
    case LIST_SUCCES: 
      return action.payload
    case LIST_FAILURE:
      return []
    default:
      throw new Error()
  }
}

I've tried multiple libraries but i'm just unable to write a test. How can i write Posts.test.js to check if post are fetched and displayed, i'm triggering async fetchList after first mount of the component (so it is not componentDidMount), and after data are fetched i dispatch action from that async function and update the list.

1 Answer 1

1

Here is the unit test solution:

index.tsx:

import React, { useReducer, useEffect } from 'react';
import { listReducer, fetchList } from './reducer';

export const Posts = () => {
  const [list, dispatch] = useReducer(listReducer, []);

  useEffect(() => {
    fetchList(dispatch);
  }, []);

  return (
    <ul>
      {list.map((el) => (
        <li key={el.id}>{el.title}</li>
      ))}
    </ul>
  );
};

reducer.ts:

import axios from 'axios';
const LIST_SUCCES = 'LIST_SUCCES';
const LIST_FAILURE = 'LIST_FAILURE';

export const fetchList = async (dispatch) => {
  try {
    const result = await axios.get('/list/'); /* AXIOS */
    dispatch({ type: LIST_SUCCES, payload: result.data.list });
  } catch (error) {
    dispatch({ type: LIST_FAILURE });
  }
};

export const listReducer = (state, action) => {
  switch (action.type) {
    case LIST_SUCCES:
      return action.payload;
    case LIST_FAILURE:
      return [];
    default:
      throw new Error();
  }
};

index.spec.tsx:

import React from 'react';
import { Posts } from './';
import { mount } from 'enzyme';
import axios from 'axios';
import { act } from 'react-dom/test-utils';

describe('Posts', () => {
  afterAll(() => {
    jest.restoreAllMocks();
  });
  it('should render list correctly', async () => {
    const mResponse = { data: { list: [{ id: 1, title: 'jest' }] } };
    jest.spyOn(axios, 'get').mockResolvedValueOnce(mResponse);
    const wrapper = mount(<Posts></Posts>);
    expect(wrapper.find('ul').children()).toHaveLength(0);
    await act(async () => {
      await new Promise((resolve) => setTimeout(resolve, 0));
    });
    wrapper.update();
    expect(wrapper.find('ul').children()).toHaveLength(1);
    expect(wrapper).toMatchInlineSnapshot(`
      <Component>
        <ul>
          <li
            key="1"
          >
            jest
          </li>
        </ul>
      </Component>
    `);
  });

  it('should render empty list when request list data failed', async () => {
    const mError = new Error('Internal server error');
    jest.spyOn(axios, 'get').mockRejectedValueOnce(mError);
    const wrapper = mount(<Posts></Posts>);
    expect(wrapper.find('ul').children()).toHaveLength(0);
    await act(async () => {
      await new Promise((resolve) => setTimeout(resolve, 0));
    });
    wrapper.update();
    expect(wrapper.find('ul').children()).toHaveLength(0);
    expect(wrapper).toMatchInlineSnapshot(`
      <Component>
        <ul />
      </Component>
    `);
  });
});

Unit test result with coverage report:

PASS  src/stackoverflow/59197574/index.spec.tsx (12.494s)
  Posts
    ✓ should render list correctly (105ms)
    ✓ should render empty list when request list data failed (37ms)

 › 1 snapshot written.
------------|----------|----------|----------|----------|-------------------|
File        |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
------------|----------|----------|----------|----------|-------------------|
All files   |    95.83 |    66.67 |      100 |       95 |                   |
 index.tsx  |      100 |      100 |      100 |      100 |                   |
 reducer.ts |    92.86 |    66.67 |      100 |    91.67 |                21 |
------------|----------|----------|----------|----------|-------------------|
Snapshot Summary
 › 1 snapshot written from 1 test suite.

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   1 written, 1 passed, 2 total
Time:        14.409s

Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59197574

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.